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 /*****************************************************************************/
57 /*****************************************************************************/
59 /*****************************************************************************/
63 /* Bitset of flags that switch the different optimizer passes */
64 unsigned long OptDisable = 0;
68 /* Bitmapped flags for the Flags field in the Line struct */
69 #define OF_CODE 0x0001 /* This line is in a code segment */
71 /* Pointer to first code line */
72 static Line* FirstCode;
75 static Line** Labels = 0; /* Pointers to label lines */
76 static unsigned LabelCount = 0; /* Count of local labels found */
78 /* A collection of lines */
79 typedef struct LineColl_ LineColl;
81 unsigned Count; /* Count of lines in the collection */
82 unsigned Max; /* Maximum count of lines */
83 Line* Lines[1]; /* Lines, dynamically allocated */
88 /* Calculate the element count of a table */
89 #define COUNT(T) (sizeof (T) / sizeof (T [0]))
91 /* Macro to increment and decrement register contents if they're valid */
92 #define INC(reg,val) if ((reg) >= 0) (reg) = ((reg) + val) & 0xFF
93 #define DEC(reg,val) if ((reg) >= 0) (reg) = ((reg) - val) & 0xFF
95 /* Defines for the conditions in a compare */
107 /* Defines for registers */
108 #define REG_NONE 0x00
112 #define REG_AX (REG_A | REG_X)
113 #define REG_ALL (REG_A | REG_X | REG_Y)
115 /* Description of the commands */
116 static const struct {
117 const char* Insn; /* Instruction */
118 unsigned char FullMatch; /* Match full instuction? */
119 unsigned char Use; /* Registers used */
120 unsigned char Load; /* Registers loaded */
122 { "\tadc\t", 0, REG_A, REG_NONE },
123 { "\tand\t", 0, REG_A, REG_NONE },
124 { "\tasl\ta", 1, REG_A, REG_NONE },
125 { "\tasl\t", 0, REG_NONE, REG_NONE },
126 { "\tclc", 1, REG_NONE, REG_NONE },
127 { "\tcld", 1, REG_NONE, REG_NONE },
128 { "\tcli", 1, REG_NONE, REG_NONE },
129 { "\tcmp\t", 0, REG_A, REG_NONE },
130 { "\tcpx\t", 0, REG_X, REG_NONE },
131 { "\tcpy\t", 0, REG_Y, REG_NONE },
132 { "\tdea", 1, REG_A, REG_NONE },
133 { "\tdec\ta", 1, REG_A, REG_NONE },
134 { "\tdec\t", 0, REG_NONE, REG_NONE },
135 { "\tdex", 1, REG_X, REG_NONE },
136 { "\tdey", 1, REG_Y, REG_NONE },
137 { "\teor\t", 0, REG_A, REG_NONE },
138 { "\tina", 1, REG_A, REG_NONE },
139 { "\tinc\ta", 1, REG_A, REG_NONE },
140 { "\tinc\t", 0, REG_NONE, REG_NONE },
141 { "\tinx", 1, REG_X, REG_NONE },
142 { "\tiny", 1, REG_Y, REG_NONE },
143 { "\tjsr\tbool", 0, REG_NONE, REG_AX },
144 { "\tjsr\tdecaxy", 1, REG_ALL, REG_AX },
145 { "\tjsr\tdecax", 0, REG_AX, REG_AX },
146 { "\tjsr\tldax0sp", 1, REG_Y, REG_AX },
147 { "\tjsr\tldaxysp", 1, REG_Y, REG_AX },
148 { "\tjsr\tpusha", 1, REG_A, REG_Y },
149 { "\tjsr\tpusha0", 1, REG_A, REG_X | REG_Y },
150 { "\tjsr\tpushax", 1, REG_AX, REG_Y },
151 { "\tjsr\tpushw0sp", 1, REG_NONE, REG_ALL },
152 { "\tjsr\tpushwysp", 1, REG_Y, REG_ALL },
153 { "\tjsr\ttosicmp", 1, REG_AX, REG_ALL },
154 { "\tlda\t", 0, REG_NONE, REG_A },
155 { "\tldax\t", 0, REG_NONE, REG_AX },
156 { "\tldx\t", 0, REG_NONE, REG_X },
157 { "\tldy\t", 0, REG_NONE, REG_Y },
158 { "\tlsr\ta", 1, REG_A, REG_NONE },
159 { "\tlsr\t", 0, REG_NONE, REG_NONE },
160 { "\tnop", 1, REG_NONE, REG_NONE },
161 { "\tora\t", 0, REG_A, REG_NONE },
162 { "\tpha", 1, REG_A, REG_NONE },
163 { "\tphp", 1, REG_NONE, REG_NONE },
164 { "\tpla", 1, REG_NONE, REG_A },
165 { "\tplp", 1, REG_NONE, REG_NONE },
166 { "\trol\ta", 1, REG_A, REG_A },
167 { "\trol\t", 0, REG_NONE, REG_NONE },
168 { "\tror\ta", 1, REG_A, REG_A },
169 { "\tror\t", 0, REG_NONE, REG_NONE },
170 { "\tsbc\t", 0, REG_A, REG_NONE },
171 { "\tsec", 1, REG_NONE, REG_NONE },
172 { "\tsed", 1, REG_NONE, REG_NONE },
173 { "\tsei", 1, REG_NONE, REG_NONE },
174 { "\tsta\t", 0, REG_A, REG_NONE },
175 { "\tstx\t", 0, REG_X, REG_NONE },
176 { "\tsty\t", 0, REG_Y, REG_NONE },
177 { "\tstz\t", 0, REG_NONE, REG_NONE },
178 { "\ttax", 1, REG_A, REG_X },
179 { "\ttay", 1, REG_A, REG_Y },
180 { "\ttrb\t", 0, REG_A, REG_NONE },
181 { "\ttsb\t", 0, REG_A, REG_NONE },
182 { "\ttsx", 1, REG_NONE, REG_X },
183 { "\ttxa", 1, REG_X, REG_A },
184 { "\ttya", 1, REG_Y, REG_A },
189 /* Table with the compare suffixes */
190 static const char CmpSuffixTab [][4] = {
191 "eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule"
194 /* Table used to invert a condition, indexed by condition */
195 static const unsigned char CmpInvertTab [] = {
197 CMP_LE, CMP_LT, CMP_GE, CMP_GT,
198 CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT
201 /* Table to show which compares are signed (use the N flag) */
202 static const char CmpSignedTab [] = {
203 0, 0, 1, 1, 1, 1, 0, 0, 0, 0
208 /* Lists of branches */
209 static const char* ShortBranches [] = {
220 static const char* LongBranches [] = {
234 /*****************************************************************************/
236 /*****************************************************************************/
240 static unsigned EstimateSize (Line* L);
241 /* Estimate the size of an instruction */
243 static int IsLocalLabel (const Line* L);
244 /* Return true if the line is a local label line */
246 static unsigned GetLabelNum (const char* L);
247 /* Return the label number of a label line */
249 static unsigned RVUInt1 (Line* L, LineColl* LC, unsigned Used, unsigned Unused);
250 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
252 static Line* NewLineAfter (Line* LineBefore, const char* Format, ...) attribute ((format(printf,2,3)));
253 /* Create a new line, insert it after L and return it. The new line is marked
257 static Line* ReplaceLine (Line* L, const char* Format, ...)
258 attribute ((format(printf,2,3)));
259 /* Replace one line by another */
263 /*****************************************************************************/
265 /*****************************************************************************/
269 static Line* NewLineAfter (Line* LineBefore, const char* Format, ...)
270 /* Create a new line, insert it after L and return it. The new line is marked
276 /* Format the new line and add it */
278 va_start (ap, Format);
279 L = NewCodeLineAfter (LineBefore, Format, ap);
282 /* Make the line a code line */
285 /* Estimate the code size */
286 L->Size = EstimateSize (L);
288 /* Return the new line */
294 static Line* NewLabelAfter (Line* L, unsigned Label)
295 /* Add a new line with a definition of a local label after the line L */
299 /* Create the label */
300 sprintf (Buf, "L%04X:", Label);
302 /* Create a new line */
303 L = NewLineAfter (L, Buf);
305 /* Insert this label into the label list */
308 /* Return the new line */
314 static void FreeLine (Line* L)
315 /* Remove a line from the list and free it */
317 /* If this is a label line, remove it from the label list */
318 if (IsLocalLabel (L)) {
319 Labels [GetLabelNum (L->Line)] = 0;
322 /* Unlink the line */
328 static Line* ReplaceLine (Line* L, const char* Format, ...)
329 /* Replace one line by another */
334 /* Format the new line */
336 va_start (ap, Format);
337 xvsprintf (S, sizeof (S), Format, ap);
340 /* Get the length of the new line */
343 /* We can copy the line if the old line has space enough */
346 /* Just copy the new line, but don't update the length */
347 memcpy (L->Line, S, Len);
348 L->Line [Len] = '\0';
352 /* We must allocate new space */
353 Line* NewLine = xmalloc (sizeof (Line) + Len);
355 /* Set the values in the new struct */
356 NewLine->Flags = L->Flags;
357 NewLine->Index = L->Index;
358 NewLine->Size = L->Size; /* Hmm ... */
360 memcpy (NewLine->Line, S, Len + 1);
362 /* Replace the old struct in the list */
363 NewLine->Next = L->Next;
365 NewLine->Next->Prev = NewLine;
370 NewLine->Prev = L->Prev;
372 NewLine->Prev->Next = NewLine;
378 /* Free the old struct */
383 /* Estimate the new size */
384 if (L->Flags & OF_CODE) {
385 L->Size = EstimateSize (L);
388 /* Return the line */
394 static Line* PrevCodeLine (Line* L)
395 /* Return the previous line containing code */
399 if (L->Flags & OF_CODE && L->Line [0] != '+') {
409 static Line* NextCodeSegLine (Line* L)
410 /* Return the next line in the code segment */
414 if (L->Flags & OF_CODE) {
424 static Line* NextCodeLine (Line* L)
425 /* Return the next line containing code */
429 if ((L->Flags & OF_CODE) != 0 && L->Line [0] != '+') {
439 static Line* NextInstruction (Line* L)
440 /* Return the next line containing code, ignoring labels. */
443 L = NextCodeLine (L);
444 } while (L && (L->Line[0] == '+' || IsLocalLabel(L)));
450 static void FreeLines (Line* Start, Line* End)
451 /* Delete all lines from Start to End, both inclusive */
456 Start = NextCodeSegLine (Start);
463 /*****************************************************************************/
464 /* Line Collections */
465 /*****************************************************************************/
469 static LineColl* NewLineColl (unsigned Size)
470 /* Create a new line collection and return it */
472 /* Allocate memory */
473 LineColl* LC = xmalloc (sizeof (LineColl) + sizeof (Line) * (Size-1));
475 /* Initialize members */
479 /* Return the new collection */
485 static void FreeLineColl (LineColl* LC)
486 /* Delete a line collection */
493 static int LCAddLine (LineColl* LC, Line* L)
494 /* Add a line. Return 0 if no space available, return 1 otherwise */
496 /* Check if there is enough space available */
497 if (LC->Count >= LC->Max) {
498 /* No room available */
503 LC->Lines [LC->Count++] = L;
511 static int LCHasLine (LineColl* LC, Line* L)
512 /* Check if the given line is in the collection */
515 for (I = 0; I < LC->Count; ++I) {
516 if (LC->Lines[I] == L) {
525 /*****************************************************************************/
526 /* Test a line for several things */
527 /*****************************************************************************/
531 static int IsLocalLabel (const Line* L)
532 /* Return true if the line is a local label line */
534 return (L->Line [0] == 'L' && isxdigit (L->Line [1]));
539 static int IsExtLabel (const Line* L)
540 /* Return true if the line is an external label line */
542 return (L->Line [0] == '_');
547 static int IsLabel (const Line* L)
548 /* Return true if the line is a label line */
550 return (L->Line [0] == 'L' && isxdigit (L->Line [1])) ||
551 (L->Line [0] == '_');;
556 static int IsHintLine (const Line* L)
557 /* Return true if the line contains an optimizer hint */
559 return L->Line [0] == '+';
564 static int IsSegHint (const Line* L)
565 /* Return true if the given line contains a segment hint */
567 return (L->Line [0] == '+' && strncmp (L->Line + 1, "seg:", 4) == 0);
572 static int IsHint (const Line* L, const char* Hint)
573 /* Check if the line contains a given hint */
575 return (L->Line [0] == '+' && strcmp (L->Line + 1, Hint) == 0);
580 static int IsCondJump (const Line* L)
581 /* Return true if the line contains a conditional jump */
583 return (L->Line [0] == '\t' &&
584 (strncmp (L->Line + 1, "beq\t", 4) == 0 ||
585 strncmp (L->Line + 1, "bne\t", 4) == 0 ||
586 strncmp (L->Line + 1, "jeq\t", 4) == 0 ||
587 strncmp (L->Line + 1, "jne\t", 4) == 0));
592 static int IsXAddrMode (const Line* L)
593 /* Return true if the given line does use the X register */
595 unsigned Len = strlen (L->Line);
596 return (strcmp (L->Line + Len - 3, ",x)") == 0 ||
597 strcmp (L->Line + Len - 2, ",x") == 0);
602 static int NoXAddrMode (const Line* L)
603 /* Return true if the given line does use the X register */
605 return !IsXAddrMode (L);
610 static int IsYAddrMode (const Line* L)
611 /* Return true if the given line does use the Y register */
613 unsigned Len = strlen (L->Line);
614 return (strcmp (L->Line + Len - 2, ",y") == 0);
619 static int Is16BitStore (const Line* L1, const Line* L2)
620 /* Check if L1 and L2 are a store of ax into a 16 bit location */
622 unsigned Len1 = strlen (L1->Line);
623 return (strncmp (L1->Line, "\tsta\t", 5) == 0 &&
624 strncmp (L2->Line, "\tstx\t", 5) == 0 &&
625 strncmp (L1->Line+5, L2->Line+5, Len1-5) == 0 &&
626 strcmp (L2->Line+Len1, "+1") == 0);
631 static Line* FindHint (Line* L, const char* Hint)
632 /* Search for a line with the given hint */
635 if (IsHint (L, Hint)) {
645 static unsigned GetHexNum (const char* S)
646 /* Get a hex number from a string */
650 while (isxdigit (S [I])) {
651 int C = (unsigned char) (S [I++]);
657 Val = (Val << 4) + C;
664 static unsigned GetLabelNum (const char* L)
665 /* Return the label number of a label line */
668 return GetHexNum (L+1);
673 static Line* GetTargetLine (const char* L)
674 /* Get the line with the target label of a jump. L must be a pointer to the
675 * string containing the label number.
680 /* Get the label number of the target */
681 unsigned Label = GetLabelNum (L);
682 CHECK (Label < LabelCount);
684 /* Get the line with this label */
685 Target = Labels [Label];
686 CHECK (Target != 0 && (Target->Flags & OF_CODE) != 0);
694 static unsigned GetJumpDistance (Line* L, Line* Target)
695 /* Get the distance between both lines */
697 unsigned Distance = 0;
700 if (Target->Index > L->Index) {
701 /* This is a forward jump. */
703 L = NextCodeLine (L);
705 } while (L != Target);
707 /* This is a backward jump */
709 L = PrevCodeLine (L);
711 } while (L != Target);
715 /* Return the calculated distance */
721 static int LineMatch (const Line* L, const char* Start)
722 /* Check if the start of the line matches Start */
724 return strncmp (L->Line, Start, strlen (Start)) == 0;
729 static int LineFullMatch (const Line* L, const char* Start)
730 /* Check if the matches Start */
732 return strcmp (L->Line, Start) == 0;
737 static int LineMatchX (const Line* L, const char** Start)
738 /* Check the start of the line against a list of patterns. Return the
739 * number of the pattern that matched, or -1 in case of no match.
744 if (LineMatch (L, *Start)) {
757 static int LineFullMatchX (const Line* L, const char** Start)
758 /* Check the the line against a list of patterns. Return the
759 * number of the pattern that matched, or -1 in case of no match.
764 if (LineFullMatch (L, *Start)) {
777 static int IsLoadAX (Line* L1, Line* L2)
778 /* Check if the both lines load a static variable into ax. That is, both lines
784 return LineMatch (L1, "\tlda\t") &&
785 LineMatch (L2, "\tldx\t") &&
786 strncmp (L1->Line+5, L2->Line+5, strlen (L1->Line+5)) == 0 &&
787 strcmp (L2->Line+strlen(L1->Line), "+1") == 0;
792 /*****************************************************************************/
793 /* Initial optimizer setup */
794 /*****************************************************************************/
798 static void FindCodeStart (void)
799 /* Find and remember the first line of actual code */
801 Line* L = FindHint (FirstLine, "end_of_preamble");
802 FirstCode = L? L->Next : 0;
807 static unsigned EstimateDataSize (Line* L, unsigned Chunk)
808 /* Estimate the size of a .byte, .word or .dword command */
810 unsigned Size = Chunk;
812 while ((S = strchr (S, ',')) != 0) {
821 static unsigned EstimateSize (Line* L)
822 /* Estimate the size of an instruction */
824 static const char* OneByteCmds [] = {
840 if (L->Line [0] != '\t') {
843 if (LineMatch (L, "\tldax\t")) {
844 /* Immidiate load of both, A and X */
847 if (LineMatch (L, "\tld")) {
848 OpStart = L->Line [5];
849 return (OpStart == '#' || OpStart == '(')? 2 : 3;
851 if (LineMatch (L, "\tst")) {
852 OpStart = L->Line [5];
853 return (OpStart == '(')? 2 : 3;
855 if (LineMatch (L, "\t.byte\t")) {
856 return EstimateDataSize (L, 1);
858 if (LineMatch (L, "\t.word\t")) {
859 return EstimateDataSize (L, 2);
861 if (LineMatch (L, "\t.dword\t")) {
862 return EstimateDataSize (L, 4);
864 if (LineMatchX (L, ShortBranches) >= 0) {
867 if (LineMatchX (L, LongBranches) >= 0) {
870 if (LineMatchX (L, OneByteCmds) >= 0) {
878 static void MarkCodeLines (void)
879 /* Mark all lines that are inside a code segment */
885 InCode = IsHint (L, "seg:code");
886 } else if (InCode && L->Line[0] != '\0') {
888 L->Size = EstimateSize (L);
896 static void CreateLabelList (void)
897 /* Create a list with pointers to local labels */
903 /* Get the next label number. This is also the current label count.
904 * Make some room for more labels when optimizing code.
906 LabelCount = GetLabel () + 100;
908 /* Allocate memory for the array and clear it */
909 Labels = xmalloc (LabelCount * sizeof (Line*));
910 for (I = 0; I < LabelCount; ++I) {
914 /* Walk through the code and insert all label lines */
917 if (IsLocalLabel (L)) {
918 unsigned LabelNum = GetLabelNum (L->Line);
919 CHECK (LabelNum < LabelCount);
920 Labels [LabelNum] = L;
928 static unsigned AllocLabel (void)
929 /* Get a new label. The current code does not realloc the label list, so there
930 * must be room enough in the current list.
935 /* Search for a free slot, start at 1, since 0 is "no label" */
936 for (I = 1; I < LabelCount; ++I) {
937 if (Labels[I] == 0) {
938 /* Found a free slot */
943 /* No label space available */
944 Internal ("Out of label space in the optimizer");
952 /*****************************************************************************/
953 /* Helper functions */
954 /*****************************************************************************/
958 static int GetNextCodeLines (Line* L, Line** Lines, unsigned Count)
959 /* Get a number of code lines ignoring hints and other stuff. The function
960 * returns 1 if we got the lines and 0 if we are at the end of the code
961 * segment or if we hit a label.
966 /* Get the next valid line */
968 L = NextCodeLine (L);
969 } while (L && IsHintLine (L));
971 /* Did we get one? */
972 if (L == 0 || IsLabel (L)) {
977 /* Remember the line */
987 static int FindCond (const char* Suffix)
988 /* Map a condition suffix to a code. Return the code or -1 on failure */
993 for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) {
994 if (strncmp (Suffix, CmpSuffixTab [I], strlen (CmpSuffixTab[I])) == 0) {
1006 static int CheckAndGetIntCmp (const Line* JSR, const Line* JMP)
1007 /* Helper function to check for a compare subroutine call followed by a
1008 * conditional branch. Will return the condition found, or -1 if no
1009 * or invalid condition.
1016 /* Extract the condition from the function name. */
1017 if ((Cond [0] = JSR->Line [8]) == 'u') {
1018 Cond [1] = JSR->Line [9];
1019 Cond [2] = JSR->Line [10];
1021 Tail = JSR->Line + 11;
1023 Cond [1] = JSR->Line [9];
1025 Tail = JSR->Line + 10;
1028 /* Check if this is indeed an integer function */
1029 if (strcmp (Tail, "ax") != 0) {
1034 /* Get the condition code */
1035 C = FindCond (Cond);
1041 /* Invert the code if we jump on condition not met. */
1042 if (JMP->Line [2] == 'e' && JMP->Line [3] == 'q') {
1043 /* Jumps if condition false, invert condition */
1044 C = CmpInvertTab [C];
1047 /* Return the condition code */
1053 static int TosCmpFunc (Line* L)
1054 /* Check if this is a call to one of the TOS compare functions (tosgtax).
1055 * Return the condition code or -1 if not found.
1058 if (LineMatch (L, "\tjsr\ttos") &&
1059 strcmp (L->Line+strlen(L->Line)-2, "ax") == 0) {
1061 /* Ok, found. Get the condition. */
1062 return FindCond (L->Line+8);
1073 static int IsUnsignedCmp (int Code)
1074 /* Check if this is an unsigned compare */
1077 return CmpSignedTab [Code] == 0;
1082 static void InvertZJump (Line* L)
1083 /* Invert a jeq/jne jump */
1085 if (L->Line [2] == 'n' && L->Line [3] == 'e') {
1086 /* This was a bne/jne */
1090 /* This was (hopefully) a beq/jeq */
1098 static int FindCmd (Line* L)
1102 /* Search for the known patterns */
1103 for (I = 0; I < COUNT(CmdDesc); ++I) {
1104 if (CmdDesc[I].FullMatch) {
1105 if (LineFullMatch (L, CmdDesc[I].Insn)) {
1110 if (LineMatch (L, CmdDesc[I].Insn)) {
1122 static unsigned RVUInt2 (Line* L,
1123 LineColl* LC, /* To remember visited lines */
1124 unsigned Used, /* Definitely used registers */
1125 unsigned Unused) /* Definitely unused registers */
1126 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
1130 /* Check the following instructions. We classifiy them into primary
1131 * loads (register value not used), neutral (check next instruction),
1132 * and unknown (assume register was used).
1138 /* Get the next line and follow jumps */
1141 /* Handle jumps to local labels (continue there) */
1142 if (LineMatch (L, "\tjmp\tL") || LineMatch (L, "\tbra\tL")) {
1143 /* Get the target of the jump */
1144 L = GetTargetLine (L->Line+5);
1147 /* Get the next line, skip local labels */
1149 L = NextCodeSegLine (L);
1150 } while (L && (IsLocalLabel (L) || L->Line[0] == '\0'));
1152 /* Bail out if we're done */
1153 if (L == 0 || IsExtLabel (L)) {
1154 /* End of function reached */
1158 /* Check if we had this line already. If so, bail out, if not,
1159 * add it to the list of known lines.
1161 if (LCHasLine (LC, L) || !LCAddLine (LC, L)) {
1165 } while (LineMatch (L, "\tjmp\tL") || LineMatch (L, "\tbra\tL"));
1167 /* Special handling of code hints */
1168 if (IsHintLine (L)) {
1170 if (IsHint (L, "a:-") && (Used & REG_A) == 0) {
1172 } else if (IsHint (L, "x:-") && (Used & REG_X) == 0) {
1174 } else if (IsHint (L, "y:-") && (Used & REG_Y) == 0) {
1178 /* Special handling for branches */
1179 } else if (LineMatchX (L, ShortBranches) >= 0 ||
1180 LineMatchX (L, LongBranches) >= 0) {
1181 const char* Target = L->Line+5;
1182 if (Target[0] == 'L') {
1183 /* Jump to local label. Check the register usage starting at
1184 * the branch target and at the code following the branch.
1185 * All registers that are unused in both execution flows are
1186 * returned as unused.
1189 U2 = RVUInt1 (GetTargetLine (Target), LC, Used, Unused);
1190 U1 = RVUInt1 (L, LC, Used, Unused);
1191 return U1 | U2; /* Used in any of the branches */
1195 /* Search for the instruction in this line */
1198 /* If we don't find it, assume all other registers are used */
1203 /* Evaluate the use flags, check for addressing modes */
1205 if (IsXAddrMode (L)) {
1207 } else if (IsYAddrMode (L)) {
1211 /* Remove registers that were already new loaded */
1214 /* Remember the remaining registers */
1218 /* Evaluate the load flags */
1219 R = CmdDesc[I].Load;
1221 /* Remove registers that were already used */
1224 /* Remember the remaining registers */
1230 /* If we know about all registers, bail out */
1231 if ((Used | Unused) == REG_ALL) {
1237 /* Return to the caller the complement of all unused registers */
1238 return ~Unused & REG_ALL;
1243 static unsigned RVUInt1 (Line* L,
1244 LineColl* LC, /* To remember visited lines */
1245 unsigned Used, /* Definitely used registers */
1246 unsigned Unused) /* Definitely unused registers */
1247 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
1249 /* Remember the current count of the line collection */
1250 unsigned Count = LC->Count;
1252 /* Call the worker routine */
1253 unsigned R = RVUInt2 (L, LC, Used, Unused);
1255 /* Restore the old count */
1258 /* Return the result */
1264 static unsigned RegValUsed (Line* Start)
1265 /* Check the next instructions after the one in L for register usage. If
1266 * a register is used as an index, or in a store or other instruction, it
1267 * is assumed to be used. If a register is loaded with a value, before it
1268 * was used by one of the actions described above, it is assumed unused.
1269 * If the end of the lookahead is reached, all registers that are uncertain
1270 * are marked as used.
1271 * The result of the search is returned.
1276 /* Create a new line collection and enter the start line */
1277 LineColl* LC = NewLineColl (256);
1278 LCAddLine (LC, Start);
1280 /* Call the recursive subfunction */
1281 R = RVUInt1 (Start, LC, REG_NONE, REG_NONE);
1283 /* Delete the line collection */
1286 /* Return the registers used */
1292 static int RegAUsed (Line* Start)
1293 /* Check if the value in A is used. */
1295 return (RegValUsed (Start) & REG_A) != 0;
1300 static int RegXUsed (Line* Start)
1301 /* Check if the value in X is used. */
1303 return (RegValUsed (Start) & REG_X) != 0;
1308 static int RegYUsed (Line* Start)
1309 /* Check if the value in Y is used. */
1311 return (RegValUsed (Start) & REG_Y) != 0;
1316 /*****************************************************************************/
1317 /* Real optimizer routines */
1318 /*****************************************************************************/
1322 static void OptCompares1 (void)
1323 /* Try to optimize the integer compare subroutines. */
1325 Line* L2[10]; /* Line lookahead */
1326 int Cond; /* Condition to evaluate */
1327 unsigned Label; /* Local label number */
1328 unsigned Offs; /* Stack offset */
1329 Line* DelStart; /* First line to delete */
1331 Line* L = FirstCode;
1334 /* Search for compares of local byte sized variables. This looks
1346 * Replace it by a direct compare:
1354 if (LineMatch (L, "\tldy\t#$") &&
1355 GetNextCodeLines (L, L2, 7) &&
1356 LineFullMatch (L2[0], "\tldx\t#$00") &&
1357 LineFullMatch (L2[1], "\tlda\t(sp),y") &&
1358 LineFullMatch (L2[2], "\tjsr\tpushax") &&
1359 LineMatch (L2[3], "\tldy\t#$") &&
1360 LineFullMatch (L2[4], "\tldx\t#$00") &&
1361 LineFullMatch (L2[5], "\tlda\t(sp),y") &&
1362 (Cond = TosCmpFunc (L2[6])) >= 0) {
1364 /* Get the stack offset and correct it, since we will remove
1367 Offs = GetHexNum (L2[3]->Line+7) - 2;
1370 L = NewLineAfter (L, "\tlda\t(sp),y");
1371 L = NewLineAfter (L, "\tldy\t#$%02X", Offs);
1372 L = NewLineAfter (L, "\tcmp\t(sp),y");
1373 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1375 /* Remove the old cruft */
1376 FreeLines (L2[0], L2[6]);
1379 /* Compares of byte sized global variables */
1380 else if (LineFullMatch (L, "\tldx\t#$00") &&
1381 GetNextCodeLines (L, L2, 5) &&
1382 LineMatch (L2[0], "\tlda\t") &&
1383 LineFullMatch (L2[1], "\tjsr\tpushax") &&
1384 LineFullMatch (L2[2], "\tldx\t#$00") &&
1385 LineMatch (L2[3], "\tlda\t") &&
1386 (Cond = TosCmpFunc (L2[4])) >= 0) {
1389 if (IsXAddrMode (L2[0])) {
1390 /* The load is X indirect, so we may not remove the load
1391 * of the X register.
1396 L = ReplaceLine (L, L2[0]->Line);
1399 L = NewLineAfter (L, "\tcmp\t%s", L2[3]->Line+5);
1400 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1402 /* Remove the old cruft */
1403 FreeLines (DelStart, L2[4]);
1407 /* Byte sized local to global */
1408 else if (LineMatch (L, "\tldy\t#$") &&
1409 GetNextCodeLines (L, L2, 6) &&
1410 LineFullMatch (L2[0], "\tldx\t#$00") &&
1411 LineFullMatch (L2[1], "\tlda\t(sp),y") &&
1412 LineFullMatch (L2[2], "\tjsr\tpushax") &&
1413 LineFullMatch (L2[3], "\tldx\t#$00") &&
1414 LineMatch (L2[4], "\tlda\t") &&
1415 (Cond = TosCmpFunc (L2[5])) >= 0) {
1418 L = NewLineAfter (L, L2[1]->Line);
1419 L = NewLineAfter (L, "\tcmp\t%s", L2[4]->Line+5);
1420 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1422 /* Remove the old cruft */
1423 FreeLines (L2[0], L2[5]);
1427 /* Byte sized global to local */
1428 else if (LineFullMatch (L, "\tldx\t#$00") &&
1429 GetNextCodeLines (L, L2, 6) &&
1430 LineMatch (L2[0], "\tlda\t") &&
1431 LineFullMatch (L2[1], "\tjsr\tpushax") &&
1432 LineMatch (L2[2], "\tldy\t#$") &&
1433 LineFullMatch (L2[3], "\tldx\t#$00") &&
1434 LineFullMatch (L2[4], "\tlda\t(sp),y") &&
1435 (Cond = TosCmpFunc (L2[5])) >= 0) {
1437 /* Get the stack offset and correct it, since we will remove
1440 Offs = GetHexNum (L2[2]->Line+7) - 2;
1443 if (IsXAddrMode (L2[0])) {
1444 /* The load is X indirect, so we may not remove the load
1445 * of the X register.
1450 L = ReplaceLine (L, L2[0]->Line);
1453 L = NewLineAfter (L, "\tldy\t#$%02X", Offs);
1454 L = NewLineAfter (L, "\tcmp\t(sp),y");
1455 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1457 /* Remove the old cruft */
1458 FreeLines (DelStart, L2[5]);
1462 /* Search for unsigned compares against global variables. This looks
1470 * Replace that by a direct compare:
1478 else if (LineFullMatch (L, "\tjsr\tpushax") &&
1479 GetNextCodeLines (L, L2, 3) &&
1480 IsLoadAX (L2[0], L2[1]) &&
1481 (Cond = TosCmpFunc (L2[2])) >= 0 &&
1482 IsUnsignedCmp (Cond)) {
1484 /* Get a free label number */
1485 Label = AllocLabel ();
1487 /* Replace the code */
1488 L = ReplaceLine (L, "\tcpx\t%s", L2[1]->Line+5);
1489 L = NewLineAfter (L, "\tbne\tL%04X", Label);
1490 L = NewLineAfter (L, "\tcmp\t%s", L2[0]->Line+5);
1491 L = NewLabelAfter(L, Label);
1492 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1494 /* Remove the old code */
1495 FreeLines (L2[0], L2[2]);
1499 L = NextCodeLine (L);
1505 static void OptDeadJumps (void)
1506 /* Remove jumps to the following instruction */
1508 static const char* Jumps [] = {
1517 Line* L = FirstCode;
1520 /* Get a pointer to the next instruction line */
1521 Line* NextLine = NextInstruction (L);
1523 /* Is this line a jump? */
1524 int I = LineMatchX (L, Jumps);
1526 /* Yes. Get the target label, skip labels */
1527 Line* Target = NextInstruction (GetTargetLine (L->Line+5));
1529 /* If the target label is the next line, remove the jump */
1530 if (Target == NextLine) {
1535 /* Go to the next line */
1542 static void OptLoads (void)
1543 /* Remove unnecessary loads of values */
1547 Line* L = FirstCode;
1568 * This change will cost 3 cycles (one additional jump inside the
1569 * subroutine), but it saves a lot of code (6 bytes per occurrence),
1570 * so we will accept the overhead. It may even be possible to rewrite
1571 * the library routine to get rid of the additional overhead.
1573 if (LineMatch (L, "\tldy\t#$") &&
1574 GetNextCodeLines (L, L2, 5) &&
1575 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
1576 LineFullMatch (L2 [1], "\ttax") &&
1577 LineFullMatch (L2 [2], "\tdey") &&
1578 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
1579 LineFullMatch (L2 [4], "\tjsr\tpushax")) {
1581 /* Found - replace it */
1582 if (LineFullMatch (L, "\tldy\t#$01")) {
1583 /* Word at offset zero */
1585 L = ReplaceLine (L2 [4], "\tjsr\tpushw0sp");
1587 ReplaceLine (L2 [4], "\tjsr\tpushwysp");
1590 /* Delete the remaining lines */
1591 FreeLines (L2 [0], L2 [3]);
1613 * This change will cost 2 cycles, but it saves a lot of code (6 bytes
1614 * per occurrence), so we will accept the overhead. It may even be
1615 * possible to rewrite the library routine to get rid of the additional
1618 } else if (LineMatch (L, "\tldy\t#$") &&
1619 GetNextCodeLines (L, L2, 6) &&
1620 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
1621 LineFullMatch (L2 [1], "\ttax") &&
1622 LineFullMatch (L2 [2], "\tdey") &&
1623 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
1624 LineMatch (L2 [4], "\tldy\t#$") &&
1625 LineFullMatch (L2 [5], "\tjsr\tldauidx")) {
1627 /* Found - replace it */
1628 L2 [4]->Line [3] = 'x'; /* Change to ldx */
1629 if (LineFullMatch (L, "\tldy\t#$01")) {
1630 /* Word at offset zero */
1632 L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp");
1634 ReplaceLine (L2 [5], "\tjsr\tldauiysp");
1637 /* Delete the remaining lines */
1638 FreeLines (L2 [0], L2 [3]);
1649 } else if (LineFullMatch (L, "\tlda\t(sp),y") &&
1650 GetNextCodeLines (L, L2, 1) &&
1651 LineFullMatch (L2 [0], "\tjsr\tpusha")) {
1653 /* Found, replace it */
1654 L = ReplaceLine (L, "\tjsr\tpushaysp");
1664 * and replace it by:
1671 * provided that that the X register is not used later. While this is
1672 * no direct optimization, it helps with other optimizations.
1674 } else if (LineMatch (L, "\tldx\t") &&
1675 GetNextCodeLines (L, L2, 3) &&
1676 LineMatch (L2 [0], "\tlda\t") &&
1677 Is16BitStore (L2[1], L2[2]) &&
1678 !RegXUsed (L2[2])) {
1680 /* Found - replace it */
1682 NewLineAfter (L, "\tsta\t%s", L2[2]->Line+5);
1693 * and replace it by:
1702 * provided that that the X register is not used later. This code
1703 * sequence is two bytes longer, but a lot faster and it does not
1704 * use the X register, so other loads may get removed later.
1706 } else if (LineMatch (L, "\tldx\t") &&
1707 GetNextCodeLines (L, L2, 3) &&
1708 LineMatch (L2 [0], "\tlda\t") &&
1709 LineMatch (L2 [1], "\tldy\t#$") &&
1710 LineFullMatch (L2 [2], "\tjsr\tstaxysp") &&
1711 !RegXUsed (L2[2])) {
1713 /* Found - replace it */
1715 L = NewLineAfter (L, "\tldy\t#$%02X", GetHexNum (L2[1]->Line+7)+1);
1716 L = NewLineAfter (L, "\tsta\t(sp),y");
1717 L = NewLineAfter (L, "\tdey");
1718 L = NewLineAfter (L2[0], "\tsta\t(sp),y");
1720 /* Remove the remaining lines */
1721 FreeLines (L2[1], L2[2]);
1729 * and replace it by:
1738 * provided that that the X register is not used later. This code
1739 * sequence is four bytes longer, but a lot faster and it does not
1740 * use the X register, so other loads may get removed later.
1742 } else if (LineMatch (L, "\tldx\t") &&
1743 GetNextCodeLines (L, L2, 2) &&
1744 LineMatch (L2 [0], "\tlda\t") &&
1745 LineFullMatch (L2 [1], "\tjsr\tstax0sp") &&
1746 !RegXUsed (L2[1])) {
1748 /* Found - replace it */
1750 L = NewLineAfter (L, "\tldy\t#$01");
1751 L = NewLineAfter (L, "\tsta\t(sp),y");
1752 L = NewLineAfter (L, "\tdey");
1753 L = NewLineAfter (L2[0], "\tsta\t(sp),y");
1755 /* Remove the remaining line */
1759 /* All other patterns start with this one: */
1760 if (!LineFullMatch (L, "\tldx\t#$00")) {
1770 * and replace it by:
1775 if (GetNextCodeLines (L, L2, 1) &&
1776 LineFullMatch (L2 [0], "\tjsr\tpushax")) {
1778 /* Replace the subroutine call */
1779 L = ReplaceLine (L, "\tjsr\tpusha0");
1781 /* Remove the unnecessary line */
1791 * and replace it by:
1797 else if (GetNextCodeLines (L, L2, 2) &&
1798 LineMatch (L2 [0], "\tlda\t") &&
1799 LineFullMatch (L2 [1], "\tjsr\tpushax")) {
1801 /* Be sure, X is not used in the load */
1802 if (NoXAddrMode (L2 [0])) {
1804 /* Replace the subroutine call */
1805 L2 [1] = ReplaceLine (L2 [1], "\tjsr\tpusha0");
1807 /* Remove the unnecessary load */
1810 /* L must be valid */
1822 * and replace it by:
1827 else if (GetNextCodeLines (L, L2, 2) &&
1828 LineMatch (L2 [0], "\tlda\t") &&
1829 LineMatch (L2 [1], "\tcmp\t#$")) {
1831 /* Be sure, X is not used in the load */
1832 if (NoXAddrMode (L2 [0])) {
1834 /* Remove the unnecessary load */
1837 /* L must be valid */
1848 * and replace it by:
1853 else if (GetNextCodeLines (L, L2, 2) &&
1854 LineMatch (L2 [0], "\tlda\t") &&
1855 LineFullMatch (L2 [1], "\tjsr\tbnega")) {
1857 /* Be sure, X is not used in the load */
1858 if (NoXAddrMode (L2 [0])) {
1860 /* Remove the unnecessary load */
1863 /* L must be valid */
1869 /* Go to the next line */
1870 L = NextCodeLine (L);
1876 static void OptRegLoads (void)
1877 /* Remove unnecessary loads of registers */
1883 /* Repeat this until there is nothing more to delete */
1891 /* Search for a load of X and check if the value is used later */
1892 if (LineMatch (L, "\tldx\t") &&
1894 !IsCondJump (NextInstruction (L))) {
1896 /* Remember to delete this line */
1900 /* Search for a load of A and check if the value is used later */
1901 else if (LineMatch (L, "\tlda\t") &&
1903 !IsCondJump (NextInstruction (L))) {
1905 /* Remember to delete this line */
1909 /* Search for a load of Y and check if the value is used later */
1910 else if (LineMatch (L, "\tldy\t") &&
1912 !IsCondJump (NextInstruction (L))) {
1914 /* Remember to delete this line */
1918 /* Go to the next line, delete the current if requested */
1920 L = NextCodeLine (L);
1926 } while (Deletions > 0);
1931 static int OptPtrOps1 (Line** Start)
1932 /* Optimize several pointer and array constructs - subfunction 1 */
1937 unsigned LinesToRemove;
1942 /* Use a local variable for the working line */
1945 /* Search for (23B/XXT)
1957 * and replace it by something like (24B/26T)
1971 if (!LineMatch (L, "\tlda\t") ||
1972 !GetNextCodeLines (L, L2, 4) ||
1973 !IsLoadAX (L, L2 [0]) ||
1974 !LineFullMatch (L2[1], "\tsta\tregsave") ||
1975 !LineFullMatch (L2[2], "\tstx\tregsave+1")) {
1982 if (LineMatch (L2[3], "\tjsr\tincax")) {
1983 /* Get next code lines */
1984 if (GetNextCodeLines (L2[3], &L2[4], 4) == 0) {
1985 /* Cannot get lines */
1988 Inc = GetHexNum (L2[3]->Line+10);
1992 /* Get next code lines */
1993 if (GetNextCodeLines (L2[3], &L2[4], 7) == 0) {
1994 /* Cannot get lines */
1997 if (LineFullMatch (L2[3], "\tclc") &&
1998 LineMatch (L2[4], "\tadc\t#$") &&
1999 LineFullMatch (L2[5], "\tbcc\t*+3") &&
2000 LineFullMatch (L2[6], "\tinx")) {
2001 /* Inlined increment */
2002 Inc = GetHexNum (L2[4]->Line+7);
2011 /* Check for the remainder */
2012 if (!LineMatch (L3[0], "\tsta\t") ||
2013 strcmp (L3[0]->Line+5, L->Line+5) != 0 ||
2014 !LineMatch (L3[1], "\tstx\t") ||
2015 strcmp (L3[1]->Line+5, L2[0]->Line+5) != 0 ||
2016 !LineFullMatch (L3[2], "\tlda\tregsave") ||
2017 !LineFullMatch (L3[3], "\tldx\tregsave+1")) {
2023 /* Check if AX is actually used following the code above. If not,
2024 * we don't need to load A/X from regsave. Since X will never be
2025 * used without A, check just for A.
2027 NeedLoad = RegAUsed (L3[3]);
2029 /* Special code for register variables */
2031 if (LineMatch (L, "\tlda\tregbank+") &&
2032 GetNextCodeLines (L3[3], &L3[4], 1) &&
2035 /* Remember the offset into the register bank */
2037 strcpy (Reg, L->Line+5);
2039 /* Check for several special sequences */
2040 if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
2041 /* Load char indirect */
2042 L = ReplaceLine (L, "\tldx\t#$00");
2043 L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
2044 L = NewLineAfter (L, "\tinc\t%s", Reg);
2045 L = NewLineAfter (L, "\tbne\t*+4");
2046 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
2049 } else if (LineFullMatch (L3[4], "\tsta\tptr1") &&
2050 GetNextCodeLines (L3[4], &L3[5], 3) &&
2051 LineFullMatch (L3[5], "\tstx\tptr1+1") &&
2052 LineFullMatch (L3[6], "\tldx\t#$00") &&
2053 LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
2055 /* Load char indirect, inlined */
2056 L = ReplaceLine (L, "\tldx\t#$00");
2057 L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
2058 L = NewLineAfter (L, "\tinc\t%s", Reg);
2059 L = NewLineAfter (L, "\tbne\t*+4");
2060 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
2064 } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
2065 if (GetNextCodeLines (L3[4], &L3[5], 2) &&
2066 LineMatch (L3[5], "\tlda\t") &&
2067 LineFullMatch (L3[6], "\tjsr\tstaspp")) {
2069 /* Store to pointer */
2070 L = ReplaceLine (L, L3[5]->Line);
2071 L = NewLineAfter (L, "\tldy\t#$00");
2072 L = NewLineAfter (L, "\tsta\t(%s),y", Reg);
2073 L = NewLineAfter (L, "\tinc\t%s", Reg);
2074 L = NewLineAfter (L, "\tbne\t*+4");
2075 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
2080 } else if (GetNextCodeLines (L3[4], &L3[5], 3) &&
2081 LineMatch (L3[5], "\tldy\t#$") &&
2082 LineFullMatch (L3[6], "\tlda\t(sp),y") &&
2083 LineFullMatch (L3[7], "\tjsr\tstaspp")) {
2085 /* Beware: We have to correct the stack offset, since we will
2086 * remove the pushax instruction!
2088 Offs = GetHexNum (L3[5]->Line+7) - 2;
2090 /* Store to pointer */
2091 L = ReplaceLine (L, "\tldy\t#$%02X", Offs);
2092 L = NewLineAfter (L, "\tldx\t#$00");
2093 L = NewLineAfter (L, "\tlda\t(sp),y");
2094 L = NewLineAfter (L, "\tsta\t(%s,x)", Reg);
2095 L = NewLineAfter (L, "\tinc\t%s", Reg);
2096 L = NewLineAfter (L, "\tbne\t*+4");
2097 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
2107 /* No register variable - insert the first part of the code */
2109 L = NewLineAfter (L, "\tsta\tptr1");
2111 L = NewLineAfter (L, "\tclc");
2112 L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
2113 L = NewLineAfter (L, "\tsta\t%s", L3[0]->Line+5);
2114 L = NewLineAfter (L, "\tlda\t%s", L3[1]->Line+5);
2116 L = NewLineAfter (L, "\tsta\tptr1+1");
2118 L = NewLineAfter (L, "\tadc\t#$00");
2119 L = NewLineAfter (L, "\tsta\t%s", L3[1]->Line+5);
2121 /* Check if we must really load the old value into a/x or if the
2122 * code may be replaced by something else.
2124 if (GetNextCodeLines (L3[3], &L3[4], 1)) {
2125 if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
2126 /* Load char indirect */
2127 L = NewLineAfter (L, "\tldx\t#$00");
2128 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2131 } else if (LineFullMatch (L3[4], "\tsta\tptr1") &&
2132 GetNextCodeLines (L3[4], &L3[5], 3) &&
2133 LineFullMatch (L3[5], "\tstx\tptr1+1") &&
2134 LineFullMatch (L3[6], "\tldx\t#$00") &&
2135 LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
2137 /* Load char indirect, inlined */
2138 L = NewLineAfter (L, "\tldx\t#$00");
2139 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2143 } else if (LineFullMatch (L3[4], "\tjsr\tldaxi")) {
2144 /* Load word indirect */
2145 L = NewLineAfter (L, "\tldy\t#$01");
2146 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2147 L = NewLineAfter (L, "\ttax");
2148 L = NewLineAfter (L, "\tdey");
2149 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2153 } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
2154 if (GetNextCodeLines (L3[4], &L3[5], 2) &&
2155 LineMatch (L3[5], "\tlda\t") &&
2156 LineFullMatch (L3[6], "\tjsr\tstaspp")) {
2158 /* Store to pointer */
2159 L = NewLineAfter (L, L3[5]->Line);
2160 L = NewLineAfter (L, "\tldy\t#$00");
2161 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2165 } else if (GetNextCodeLines (L3[4], &L3[5], 3) &&
2166 LineMatch (L3[5], "\tldy\t#$") &&
2167 LineFullMatch (L3[6], "\tlda\t(sp),y") &&
2168 LineFullMatch (L3[7], "\tjsr\tstaspp")) {
2170 /* Beware: We have to correct the stack offset, since we will
2171 * remove the pushax instruction!
2173 sprintf (L3[5]->Line+7, "%02X", GetHexNum (L3[5]->Line+7)-2);
2175 /* Store to pointer */
2176 L = NewLineAfter (L, L3[5]->Line);
2177 L = NewLineAfter (L, L3[6]->Line);
2178 L = NewLineAfter (L, "\tldy\t#$00");
2179 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2188 /* If we need to load a/x, add the code */
2190 L = NewLineAfter (L, "\tlda\tptr1");
2191 L = NewLineAfter (L, "\tldx\tptr1+1");
2195 /* Remove the code that is no longer needed */
2196 FreeLines (L2[0], L2[LinesToRemove-1]);
2198 /* Return the new line and success */
2199 *Start = NextCodeLine (L);
2205 static int OptPtrOps2 (Line** Start)
2206 /* Optimize several pointer and array constructs - subfunction 2 */
2211 unsigned LinesToRemove;
2216 /* Use a local variable for the working line */
2219 /* Same as subfunction 1 but for local variables. */
2220 if (LineMatch (L, "\tldy\t#$") == 0) {
2224 /* Get the stack offset. The offset points to the high byte, correct that. */
2225 Offs = GetHexNum (L->Line+7) - 1;
2227 /* Check for the actual sequences */
2228 if (GetNextCodeLines (L, L2, 7) &&
2229 LineFullMatch (L2[0], "\tjsr\tldaxysp") &&
2230 LineFullMatch (L2[1], "\tsta\tregsave") &&
2231 LineFullMatch (L2[2], "\tstx\tregsave+1") &&
2232 LineMatch (L2[3], "\tjsr\tincax")) {
2234 /* Non inlined version */
2235 Inc = GetHexNum (L2[3]->Line+10);
2237 /* Check for stack offset zero */
2238 if (LineFullMatch (L2[4], "\tjsr\tstax0sp") &&
2239 LineFullMatch (L2[5], "\tlda\tregsave") &&
2240 LineFullMatch (L2[6], "\tldx\tregsave+1")) {
2244 } else if (GetNextCodeLines (L2[6], &L2[7], 1) &&
2245 LineMatch (L2[4], "\tldy\t#$") &&
2246 GetHexNum (L2[4]->Line+7) == Offs &&
2247 LineFullMatch (L2[5], "\tjsr\tstaxysp") &&
2248 LineFullMatch (L2[6], "\tlda\tregsave") &&
2249 LineFullMatch (L2[7], "\tldx\tregsave+1")) {
2258 } else if (GetNextCodeLines (L, L2, 13) &&
2259 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
2260 LineFullMatch (L2[1], "\ttax") &&
2261 LineFullMatch (L2[2], "\tdey") &&
2262 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
2263 LineFullMatch (L2[4], "\tsta\tregsave") &&
2264 LineFullMatch (L2[5], "\tstx\tregsave+1") &&
2265 LineFullMatch (L2[6], "\tclc") &&
2266 LineMatch (L2[7], "\tadc\t#$") &&
2267 LineFullMatch (L2[8], "\tbcc\t*+3") &&
2268 LineFullMatch (L2[9], "\tinx")) {
2270 /* Inlined version */
2271 Inc = GetHexNum (L2[7]->Line+7);
2273 /* Check for stack offset zero */
2274 if (LineFullMatch (L2[10], "\tjsr\tstax0sp") &&
2275 LineFullMatch (L2[11], "\tlda\tregsave") &&
2276 LineFullMatch (L2[12], "\tldx\tregsave+1")) {
2280 } else if (GetNextCodeLines (L2[12], &L2[13], 1) &&
2281 LineMatch (L2[10], "\tldy\t#$") &&
2282 GetHexNum (L2[10]->Line+7) == Offs &&
2283 LineFullMatch (L2[11], "\tjsr\tstaxysp") &&
2284 LineFullMatch (L2[12], "\tlda\tregsave") &&
2285 LineFullMatch (L2[13], "\tldx\tregsave+1")) {
2298 /* Get a pointer to the last line of the preceding sequence */
2299 L3 = &L2[LinesToRemove-1];
2301 /* Check if AX is actually used following the code above. If not,
2302 * we don't need to load A/X from regsave. Since X will never by
2303 * used without A, check just for A.
2305 NeedLoad = RegAUsed (L3[0]);
2307 /* Replace the ldy instruction, offset must point to the low byte */
2308 sprintf (L->Line+7, "%02X", Offs);
2310 /* Insert the first part of the code */
2311 L = NewLineAfter (L, "\tlda\t(sp),y");
2313 L = NewLineAfter (L, "\tsta\tptr1");
2315 L = NewLineAfter (L, "\tclc");
2316 L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
2317 L = NewLineAfter (L, "\tsta\t(sp),y");
2318 L = NewLineAfter (L, "\tiny");
2319 L = NewLineAfter (L, "\tlda\t(sp),y");
2321 L = NewLineAfter (L, "\tsta\tptr1+1");
2323 L = NewLineAfter (L, "\tadc\t#$00");
2324 L = NewLineAfter (L, "\tsta\t(sp),y");
2326 /* Check if we must really load the old value into a/x or if the
2327 * code may be replaced by something else.
2329 if (GetNextCodeLines (L3[0], &L3[1], 1)) {
2330 if (LineFullMatch (L3[1], "\tjsr\tldaui")) {
2331 /* Load char indirect */
2332 L = NewLineAfter (L, "\tldx\t#$00");
2333 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2336 } else if (LineFullMatch (L3[1], "\tsta\tptr1") &&
2337 GetNextCodeLines (L3[1], &L3[2], 3) &&
2338 LineFullMatch (L3[2], "\tstx\tptr1+1") &&
2339 LineFullMatch (L3[3], "\tldx\t#$00") &&
2340 LineFullMatch (L3[4], "\tlda\t(ptr1,x)")) {
2342 /* Load char indirect, inlined */
2343 L = NewLineAfter (L, "\tldx\t#$00");
2344 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2348 } else if (LineFullMatch (L3[1], "\tjsr\tldaxi")) {
2349 /* Load word indirect */
2350 L = NewLineAfter (L, "\tldy\t#$01");
2351 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2352 L = NewLineAfter (L, "\ttax");
2353 L = NewLineAfter (L, "\tdey");
2354 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2358 } else if (LineFullMatch (L3[1], "\tjsr\tpushax")) {
2359 if (GetNextCodeLines (L3[1], &L3[2], 2) &&
2360 LineMatch (L3[2], "\tlda\t") &&
2361 LineFullMatch (L3[3], "\tjsr\tstaspp")) {
2363 /* Store to pointer */
2364 L = NewLineAfter (L, L3[2]->Line);
2365 L = NewLineAfter (L, "\tldy\t#$00");
2366 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2370 } else if (GetNextCodeLines (L3[1], &L3[2], 3) &&
2371 LineMatch (L3[2], "\tldy\t#$") &&
2372 LineFullMatch (L3[3], "\tlda\t(sp),y") &&
2373 LineFullMatch (L3[4], "\tjsr\tstaspp")) {
2375 /* Beware: We have to correct the stack offset, since we will
2376 * remove the pushax instruction!
2378 sprintf (L3[2]->Line+7, "%02X", GetHexNum (L3[2]->Line+7)-2);
2380 /* Store to pointer */
2381 L = NewLineAfter (L, L3[2]->Line);
2382 L = NewLineAfter (L, L3[3]->Line);
2383 L = NewLineAfter (L, "\tldy\t#$00");
2384 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2393 /* If we need to load a/x, add the code */
2395 L = NewLineAfter (L, "\tlda\tptr1");
2396 L = NewLineAfter (L, "\tldx\tptr1+1");
2399 /* Remove the code that is no longer needed */
2400 FreeLines (L2[0], L2[LinesToRemove-1]);
2402 /* Return the new line and success */
2403 *Start = NextCodeLine (L);
2409 static void OptPtrOps (void)
2410 /* Optimize several pointer and array constructs */
2414 Line* L = FirstCode;
2417 if (OptPtrOps1 (&L)) {
2419 } else if (OptPtrOps2 (&L)) {
2423 /* Search for the following sequence:
2431 * and replace it by:
2438 else if (LineFullMatch (L, "\tlda\tregsave") && /* Match on start */
2439 GetNextCodeLines (L, L2, 4) && /* Fetch next lines */
2440 LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */
2441 LineFullMatch (L2 [1], "\tjsr\tpushax") &&
2442 LineMatch (L2 [2], "\tlda\t#$") &&
2443 LineFullMatch (L2 [3], "\tjsr\tstaspp")) {
2445 /* Found the sequence, replace it */
2446 L = ReplaceLine (L, L2 [2]->Line); /* lda #$.. */
2447 L2 [0] = ReplaceLine (L2 [0], "\tldy\t#$00");
2448 L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
2450 /* Free the remaining lines */
2451 FreeLines (L2 [2], L2 [3]);
2454 /* Search for the following sequence:
2460 * and replace it by:
2466 else if (LineFullMatch (L, "\tlda\tregsave") && /* Match on start */
2467 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
2468 LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */
2469 LineFullMatch (L2 [1], "\tjsr\tldaui")) {
2471 /* Found the sequence, replace it */
2472 L = ReplaceLine (L, "\tldx\t#$00");
2473 L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
2475 /* Free the remaining lines */
2480 * Search for the following sequence:
2489 * and replace it by:
2500 else if (LineFullMatch (L, "\tlda\tregsave") &&
2501 GetNextCodeLines (L, L2, 5) &&
2502 LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
2503 LineFullMatch (L2 [1], "\tjsr\tpushax") &&
2504 LineMatch (L2 [2], "\tldx\t#$") &&
2505 LineMatch (L2 [3], "\tlda\t#$") &&
2506 LineFullMatch (L2 [4], "\tjsr\tstaxspp")) {
2508 /* Found the sequence, replace it */
2509 L = ReplaceLine (L, "\tldy\t#$01");
2510 L2 [0] = ReplaceLine (L2 [0], L2 [2]->Line);
2511 L2 [0]->Line [3] = 'a';
2512 L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
2513 L2 [4] = ReplaceLine (L2 [4], L2 [3]->Line);
2514 L2 [2] = ReplaceLine (L2 [2], "\ttax");
2515 L2 [3] = ReplaceLine (L2 [3], "\tdey");
2516 L = NewLineAfter (L2 [4], "\tsta\t(regsave),y");
2520 * Search for the following sequence:
2529 * and replace it by:
2535 else if (LineFullMatch (L, "\tlda\tregsave") &&
2536 GetNextCodeLines (L, L2, 5) &&
2537 LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
2538 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2539 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2540 LineFullMatch (L2 [3], "\tldx\t#$00") &&
2541 LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
2543 /* Found the sequence, replace it */
2544 L = ReplaceLine (L, "\tldx\t#$00");
2545 L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
2547 /* Remove the remaining lines */
2548 FreeLines (L2 [1], L2 [4]);
2551 /* Search for the following sequence:
2557 * and replace it by:
2566 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2567 GetNextCodeLines (L, L2, 2) &&
2568 LineMatch (L2 [0], "\tlda\t") &&
2569 LineFullMatch (L2 [1], "\tjsr\tstaspp")) {
2571 /* Found the sequence, replace it */
2572 L = ReplaceLine (L, "\tsta\tptr1");
2573 L2 [1] = ReplaceLine (L2 [1], L2 [0]->Line); /* lda ... */
2574 L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
2575 L2 [2] = NewLineAfter (L2 [1], "\tldy\t#$00");
2576 L = NewLineAfter (L2 [2], "\tsta\t(ptr1),y");
2579 /* Search for the following sequence:
2586 * and replace it by:
2595 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2596 GetNextCodeLines (L, L2, 3) &&
2597 LineMatch (L2 [0], "\tlda\t") &&
2598 LineMatch (L2 [1], "\tldy\t#$") &&
2599 LineFullMatch (L2 [2], "\tjsr\tstaspidx")) {
2601 /* Found the sequence, replace it */
2602 L = ReplaceLine (L, "\tsta\tptr1");
2603 L = NewLineAfter (L, "\tstx\tptr1+1");
2604 L2 [2] = ReplaceLine (L2 [2], "\tsta\t(ptr1),y");
2607 /* Search for the following sequence:
2614 * and replace it by:
2623 * Beware: Since we remove a call to a function that changes the stack
2624 * pointer, we have to adjust the stack address for the lda.
2627 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2628 GetNextCodeLines (L, L2, 3) &&
2629 LineMatch (L2 [0], "\tldy\t#$") &&
2630 LineFullMatch (L2 [1], "\tlda\t(sp),y") &&
2631 LineFullMatch (L2 [2], "\tjsr\tstaspp")) {
2633 /* Found the sequence, replace it. First create a new load
2634 * instruction for the changed stack offset.
2637 sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
2638 L = ReplaceLine (L, "\tsta\tptr1");
2639 L2 [1] = ReplaceLine (L2 [1], Buf); /* ldy ... */
2640 L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
2641 L2 [2] = ReplaceLine (L2 [2], "\tlda\t(sp),y");
2642 L2 [3] = NewLineAfter (L2 [2], "\tldy\t#$00");
2643 L = NewLineAfter (L2 [3], "\tsta\t(ptr1),y");
2646 /* Search for the following sequence:
2654 * and replace it by:
2663 * Beware: Since we remove a call to a function that changes the stack
2664 * pointer, we have to adjust the stack address for the lda.
2667 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2668 GetNextCodeLines (L, L2, 4) &&
2669 LineMatch (L2 [0], "\tldy\t#$") &&
2670 LineFullMatch (L2 [1], "\tlda\t(sp),y") &&
2671 LineMatch (L2 [2], "\tldy\t#$") &&
2672 LineFullMatch (L2 [3], "\tjsr\tstaspidx")) {
2674 /* Found the sequence, replace it. First create a new load
2675 * instruction for the changed stack offset.
2678 sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
2679 L = ReplaceLine (L, "\tsta\tptr1");
2680 L = NewLineAfter (L, "\tstx\tptr1+1");
2681 L2 [0] = ReplaceLine (L2 [0], Buf); /* ldy ... */
2682 L2 [3] = ReplaceLine (L2 [3], "\tsta\t(ptr1),y");
2685 /* Search for the following sequence:
2698 * and replace it by:
2706 * The load of X may be omitted if X is not used below.
2708 else if (LineMatch (L, "\tldax\t_") &&
2709 GetNextCodeLines (L, L2, 9) &&
2710 LineMatch (L2 [0], "\tldy\t#$") &&
2711 LineFullMatch (L2 [1], "\tclc") &&
2712 LineFullMatch (L2 [2], "\tadc\t(sp),y") &&
2713 LineFullMatch (L2 [3], "\tbcc\t*+3") &&
2714 LineFullMatch (L2 [4], "\tinx") &&
2715 LineFullMatch (L2 [5], "\tsta\tptr1") &&
2716 LineFullMatch (L2 [6], "\tstx\tptr1+1") &&
2717 LineFullMatch (L2 [7], "\tldx\t#$00") &&
2718 LineFullMatch (L2 [8], "\tlda\t(ptr1,x)")) {
2720 /* Found the sequence, replace it */
2722 strcpy (Label, L->Line + 6); /* Remember the label */
2723 L = ReplaceLine (L, L2 [0]->Line); /* ldy .. */
2724 L = NewLineAfter (L, "\tlda\t(sp),y");
2725 L = NewLineAfter (L, "\ttay");
2726 if (RegXUsed (L2[8])) {
2727 L = NewLineAfter (L, "\tldx\t#$00");
2729 L = NewLineAfter (L, "\tlda\t%s,y", Label);
2731 /* Remove the remaining stuff. There may be hints between the
2732 * instructions, remove them too
2734 FreeLines (L2[0], L2 [8]);
2758 * This change will cost 2 cycles, but it saves a lot of code (6 bytes
2759 * per occurrence), so we will accept the overhead. It may even be
2760 * possible to rewrite the library routine to get rid of the additional
2763 else if (LineMatch (L, "\tldy\t#$") &&
2764 GetNextCodeLines (L, L2, 6) &&
2765 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
2766 LineFullMatch (L2 [1], "\ttax") &&
2767 LineFullMatch (L2 [2], "\tdey") &&
2768 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
2769 LineMatch (L2 [4], "\tldy\t#$") &&
2770 LineFullMatch (L2 [5], "\tjsr\tldauidx")) {
2772 /* Found - replace it */
2773 L2 [4]->Line [3] = 'x'; /* Change to ldx */
2774 if (LineFullMatch (L, "\tldy\t#$01")) {
2775 /* Word at offset zero */
2777 L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp");
2779 ReplaceLine (L2 [5], "\tjsr\tldauiysp");
2782 /* Delete the remaining lines */
2783 FreeLines (L2 [0], L2 [3]);
2807 * This change will has an overhead of 10 cycles, but it saves 11(!)
2808 * bytes per invocation. Maybe we should apply only if FavourSize is
2811 else if (LineMatch (L, "\tldy\t#$") &&
2812 GetNextCodeLines (L, L2, 8) &&
2813 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
2814 LineFullMatch (L2 [1], "\ttax") &&
2815 LineFullMatch (L2 [2], "\tdey") &&
2816 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
2817 LineFullMatch (L2 [4], "\tsta\tptr1") &&
2818 LineFullMatch (L2 [5], "\tstx\tptr1+1") &&
2819 LineFullMatch (L2 [6], "\tldx\t#$00") &&
2820 LineFullMatch (L2 [7], "\tlda\t(ptr1,x)")) {
2822 /* Found - replace it */
2823 if (LineFullMatch (L, "\tldy\t#$01")) {
2824 /* Word at offset zero */
2826 L = ReplaceLine (L2 [0], "\tjsr\tldau00sp");
2828 ReplaceLine (L2 [0], "\tjsr\tldau0ysp");
2831 /* Delete the remaining lines */
2832 FreeLines (L2 [1], L2 [7]);
2854 * Provided that X is not used later.
2856 else if (LineMatch (L, "\tldy\t#$") &&
2857 GetNextCodeLines (L, L2, 6) &&
2858 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
2859 LineFullMatch (L2 [1], "\ttax") &&
2860 LineFullMatch (L2 [2], "\tdey") &&
2861 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
2862 Is16BitStore (L2[4], L2[5]) &&
2863 !RegXUsed (L2[5])) {
2865 /* Found - replace it */
2866 L2[1] = ReplaceLine (L2[1], L2[5]->Line);
2867 L2[1]->Line[3] = 'a';
2869 /* Delete the remaining lines */
2872 /* Start over at last line */
2877 L = NextCodeLine (L);
2883 static void OptRegVars (void)
2884 /* Optimize register variable uses */
2888 Line* L = FirstCode;
2891 /* Search for the following sequence:
2897 * and replace it by:
2903 if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2904 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
2905 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2906 LineFullMatch (L2 [1], "\tjsr\tldaui") &&
2907 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2908 strcmp (L2 [0]->Line + 14, "+1") == 0) {
2911 sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
2913 /* Found the sequence, replace it */
2914 L = ReplaceLine (L, "\tldx\t#$00");
2915 L2 [0] = ReplaceLine (L2 [0], Buf);
2917 /* Free the remaining lines */
2921 /* Search for the following sequence:
2930 * and replace it by:
2936 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2937 GetNextCodeLines (L, L2, 5) && /* Fetch next lines */
2938 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2939 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2940 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2941 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2942 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2943 LineFullMatch (L2 [3], "\tldx\t#$00") &&
2944 LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
2947 sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
2949 /* Found the sequence, replace it */
2950 L = ReplaceLine (L, "\tldx\t#$00");
2951 L2 [0] = ReplaceLine (L2 [0], Buf);
2953 /* Free the remaining lines */
2954 FreeLines (L2 [1], L2 [4]);
2957 /* Search for the following sequence:
2964 * and replace it by:
2971 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2972 GetNextCodeLines (L, L2, 3) && /* Fetch next lines */
2973 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2974 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2975 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2976 LineMatch (L2 [1], "\tldy\t#$") &&
2977 LineFullMatch (L2 [2], "\tjsr\tldauidx")) {
2980 sprintf (Buf, "\tlda\t(%s),y", L->Line + 5);
2982 /* Found the sequence, replace it */
2983 L = ReplaceLine (L, L2 [1]->Line);
2984 L2 [0] = ReplaceLine (L2 [0], "\tldx\t#$00");
2985 L2 [1] = ReplaceLine (L2 [1], Buf);
2987 /* Free the remaining lines */
2991 /* Search for the following sequence:
3001 * and replace it by:
3007 * The source form is not generated by the parser but by the optimizer.
3009 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
3010 GetNextCodeLines (L, L2, 6) && /* Fetch next lines */
3011 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
3012 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
3013 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
3014 LineFullMatch (L2 [1], "\tsta\tptr1") &&
3015 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
3016 LineMatch (L2 [3], "\tlda\t") &&
3017 LineMatch (L2 [4], "\tldy\t#$") &&
3018 LineMatch (L2 [5], "\tsta\t(ptr1),y")) {
3021 sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
3023 /* Found the sequence, replace it */
3024 L2 [5] = ReplaceLine (L2 [5], Buf);
3026 /* Free the remaining lines */
3027 FreeLines (L, L2 [2]);
3029 /* Make the line pointer valid again */
3033 /* Search for the following sequence:
3044 * and replace it by:
3051 * The source form is not generated by the parser but by the optimizer.
3053 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
3054 GetNextCodeLines (L, L2, 7) && /* Fetch next lines */
3055 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
3056 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
3057 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
3058 LineFullMatch (L2 [1], "\tsta\tptr1") &&
3059 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
3060 LineMatch (L2 [3], "\tldy\t#$") &&
3061 LineFullMatch (L2 [4], "\tlda\t(sp),y") &&
3062 LineMatch (L2 [5], "\tldy\t#$") &&
3063 LineMatch (L2 [6], "\tsta\t(ptr1),y")) {
3066 sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
3068 /* Found the sequence, replace it */
3069 L2 [6] = ReplaceLine (L2 [6], Buf);
3071 /* Free the remaining lines */
3072 FreeLines (L, L2 [2]);
3074 /* Make the line pointer valid again */
3079 L = NextCodeLine (L);
3085 static void OptDoubleJumps (void)
3086 /* Remove/rearrange jumps that jump to other jumps */
3088 static const char* Jumps [] = {
3099 Line* L = FirstCode;
3104 /* Is this a jump? */
3105 while ((I = LineMatchX (L, Jumps)) >= 0) {
3107 /* Yes. Get the target label */
3108 Line* Target = GetTargetLine (L->Line + 5);
3110 /* Target points to the label itself. Skip lines until we reach
3111 * one that is not a label.
3113 Target = NextInstruction (Target);
3115 /* Be sure, this line is not the same as the one the jump is
3116 * in (this happens if there is an empty loop).
3122 if (LineMatch (Target, "\tjmp\t")) {
3124 /* The target is itself a jump. If this is a short branch, get
3125 * the final target and check if it is in reach. Bail out if
3128 if (L->Line[1] == 'b') {
3129 Line* FinalTarget = GetTargetLine (Target->Line+5);
3130 FinalTarget = NextInstruction (FinalTarget);
3131 if ((D = GetJumpDistance (L, FinalTarget)) >= 123) {
3136 /* Make sure the jump does indeed point to another label.
3137 * It may happen that this is not the case for some endless
3138 * loop (while(1) and similar).
3140 if (strcmp (L->Line+5, Target->Line+5) == 0) {
3141 /* Same label, bail out */
3145 /* Use the label in the original jump instead */
3146 L = ReplaceLine (L, "%.5s%s", L->Line, Target->Line+5);
3148 } else if (I < 2 && LineMatch (Target, Jumps [I])) {
3150 /* Conditional jump. Use final label */
3151 strcpy (L->Line+5, Target->Line+5);
3159 L = NextCodeLine (L);
3165 static void OptJumpRTS (void)
3166 /* Replace jumps to an RTS by an RTS */
3168 Line* L = FirstCode;
3170 /* Is this a jump to a numbered label? */
3171 if (LineMatch (L, "\tjmp\t") && L->Line [5] == 'L' && isdigit (L->Line [6])) {
3173 /* Yes. Get the target label */
3174 Line* Target = GetTargetLine (L->Line+5);
3176 /* Target points to the label itself. Get the next line */
3177 Target = NextCodeLine (Target);
3178 if (LineFullMatch (Target, "\trts")) {
3179 /* Replace the jump by an RTS */
3180 L = ReplaceLine (L, "\trts");
3183 L = NextCodeLine (L);
3189 static void OptBoolTransforms (void)
3190 /* Try to remove the boolean transformation subroutines where they aren't
3196 const char* BranchTarget;
3198 Line* L = FirstCode;
3201 /* Search for a boolean transformer followed by a conditional jump. */
3202 if (LineMatch (L, "\tjsr\tbool") &&
3203 GetNextCodeLines (L, L2, 1) &&
3204 IsCondJump (L2 [0])) {
3206 /* Make the boolean transformer unnecessary by changing the
3207 * the conditional jump to evaluate the condition flags that
3208 * are set after the compare directly. Note: jeq jumps if
3209 * the condition is not met, jne jumps if the condition is met.
3212 /* Get the condition code */
3213 int Cond = FindCond (L->Line + 9);
3219 /* Invert the code if we jump on condition not met. */
3220 if (L2[0]->Line [2] == 'e' && L2[0]->Line [3] == 'q') {
3221 /* Jumps if condition false, invert condition */
3222 Cond = CmpInvertTab [Cond];
3225 /* For easier reading, get a pointer to the jump target */
3226 BranchTarget = L2[0]->Line+5;
3228 /* Check if we can replace the jump (sometimes we would need two
3229 * conditional jumps, we will not handle that for now since it
3230 * has some complications - both jumps may be far jumps for
3231 * example making the jumps more costly than the bool transformer
3232 * subroutine). If we cannot replace the jump, bail out.
3237 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3241 L = ReplaceLine (L, "\tjne\t%s", BranchTarget);
3245 Label = AllocLabel ();
3246 L = ReplaceLine (L, "\tbeq\tL%04X", Label);
3247 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3248 L = NewLabelAfter(L, Label);
3252 L = ReplaceLine (L, "\tjpl\t%s", BranchTarget);
3256 L = ReplaceLine (L, "\tjmi\t%s", BranchTarget);
3260 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3261 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3265 Label = AllocLabel ();
3266 L = ReplaceLine (L, "\tbeq\tL%04X", Label);
3267 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3268 L = NewLabelAfter(L, Label);
3272 L = ReplaceLine (L, "\tjcs\t%s", BranchTarget);
3276 L = ReplaceLine (L, "\tjcc\t%s", BranchTarget);
3280 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3281 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3285 Internal ("Unknown jump condition: %u", Cond);
3289 /* Remove the old stuff */
3295 L = NextCodeLine (L);
3301 static void OptCompares2 (void)
3302 /* Try to optimize the integer compare subroutines. */
3306 const char* BranchTarget;
3309 Line* L = FirstCode;
3327 if (LineMatch (L, "\tlda\t") &&
3328 GetNextCodeLines (L, L2, 5) &&
3329 IsLoadAX (L, L2[0]) &&
3330 LineFullMatch (L2[1], "\tcpx\t#$00") &&
3331 LineFullMatch (L2[2], "\tbne\t*+4") &&
3332 LineFullMatch (L2[3], "\tcmp\t#$00") &&
3333 IsCondJump (L2[4])) {
3335 /* Replace the load of X by an ora */
3336 L2[0]->Line[1] = 'o';
3337 L2[0]->Line[2] = 'r';
3338 L2[0]->Line[3] = 'a';
3340 /* Remove unneeded stuff */
3341 FreeLines (L2[1], L2[3]);
3345 /* Same for local variables: Replace
3365 else if (LineMatch (L, "\tldy\t#$") &&
3366 GetNextCodeLines (L, L2, 8) &&
3367 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
3368 LineFullMatch (L2[1], "\ttax") &&
3369 LineFullMatch (L2[2], "\tdey") &&
3370 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
3371 LineFullMatch (L2[4], "\tcpx\t#$00") &&
3372 LineFullMatch (L2[5], "\tbne\t*+4") &&
3373 LineFullMatch (L2[6], "\tcmp\t#$00") &&
3374 IsCondJump (L2[7])) {
3376 /* Replace the second load by an ora */
3377 L2[3]->Line[1] = 'o';
3378 L2[3]->Line[2] = 'r';
3379 L2[3]->Line[3] = 'a';
3381 /* Remove unneeded stuff */
3383 FreeLines (L2[4], L2[6]);
3387 /* Search for the call to a compare subroutine followed by a
3390 else if (LineMatch (L, "\tjsr\ttos") &&
3391 (L2[0] = NextCodeLine (L)) != 0 &&
3392 IsCondJump (L2[0])) {
3394 /* Extract the condition from the function name and branch */
3395 C = CheckAndGetIntCmp (L, L2[0]);
3397 /* Something is wrong */
3401 /* Replace the subroutine call by a cheaper one */
3402 L = ReplaceLine (L, "\tjsr\ttosicmp");
3404 /* For easier reading, get a pointer to the jump target */
3405 BranchTarget = L2[0]->Line+5;
3407 /* Check if we can replace the jump (sometimes we would need two
3408 * conditional jumps, we will not handle that for now since it
3409 * has some complications - both jumps may be far jumps for
3410 * example making the jumps more costly than the bool transformer
3411 * subroutine). If we cannot replace the jump, bail out.
3416 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3420 L = NewLineAfter (L, "\tjne\t%s", BranchTarget);
3424 Label = AllocLabel ();
3425 L = NewLineAfter (L, "\tbeq\tL%04X", Label);
3426 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3427 L = NewLabelAfter(L, Label);
3431 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3435 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3439 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3440 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3444 Label = AllocLabel ();
3445 L = NewLineAfter (L, "\tbeq\tL%04X", Label);
3446 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3447 L = NewLabelAfter(L, Label);
3451 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3455 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3459 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3460 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3464 Internal ("Unknown jump condition: %u", C);
3468 /* Remove the old stuff */
3473 L = NextCodeLine (L);
3479 static void OptTests (void)
3480 /* Remove unnecessary tests */
3484 static const char* BitOps [] = {
3491 /* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
3498 Line* L = FirstCode;
3501 /* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
3508 if ((LineMatch (L, "\tlda\t") ||
3509 LineMatch (L, "\tand\t") ||
3510 LineMatch (L, "\tora\t") ||
3511 LineMatch (L, "\teor\t")) &&
3512 GetNextCodeLines (L, L2, 2) &&
3513 (LineFullMatch (L2 [0], "\ttay") ||
3514 LineFullMatch (L2 [0], "\tcmp\t#$00")) &&
3515 IsCondJump (L2 [1])) {
3517 /* We can remove the tay */
3528 * and remove the tax.
3530 else if (LineMatchX (L, BitOps) >= 0 &&
3531 GetNextCodeLines (L, L2, 2) &&
3532 LineFullMatch (L2[0], "\ttax") &&
3533 IsCondJump (L2[1])) {
3535 /* Remove the tax including a hint line of there is one */
3536 if (LineFullMatch (L2[0]->Prev, "+forcetest")) {
3537 FreeLine (L2[0]->Prev);
3541 /* If the line before L loads X, this is useless and may be removed */
3542 L2[0] = PrevCodeLine (L);
3543 if (LineFullMatch (L2[0], "\tldx\t#$00")) {
3549 /* Search for the sequence
3560 else if (LineMatch (L, "\tstx\t") &&
3561 GetNextCodeLines (L, L2, 2) &&
3562 LineFullMatch (L2[0], "\tstx\ttmp1") &&
3563 LineFullMatch (L2[1], "\tora\ttmp1")) {
3565 /* Found, replace it */
3566 L = NewLineAfter (L, "\tora\t%s", L->Line+5);
3568 /* Remove remaining stuff */
3569 FreeLines (L2[0], L2[1]);
3575 L = NextCodeLine (L);
3581 static void OptBitOps (void)
3582 /* Optimize bit oeprations */
3586 /* Walk over the code */
3587 Line* L = FirstCode;
3593 * and #$yy ; adc/eor/ora
3602 * While this saves nothing here, it transforms the code to contain an
3603 * explicit register load that may be removed by the basic block
3604 * optimization later. As a special optimization for the 65C02, the
3605 * "ora" and "and" ops may be replaced by "trb" and "tsb" resp. if the
3606 * value in A is not used later.
3608 if (LineMatch (L, "\tlda\t") &&
3609 L->Line[5] != '#' &&
3610 GetNextCodeLines (L, L2, 2) &&
3611 LineMatch (L2[1], "\tsta\t") &&
3612 strcmp (L->Line+5, L2[1]->Line+5) == 0) {
3614 if (LineMatch (L2[0], "\tand\t#$")) {
3616 unsigned Val = GetHexNum (L2[0]->Line+7);
3619 /* AND with 0x00, remove the mem access */
3623 /* Replace the AND by a load */
3624 L = ReplaceLine (L2[0], "\tlda\t#$%02X", Val);
3626 } else if (Val == 0xFF) {
3628 /* AND with 0xFF, just load the value from memory */
3629 FreeLines (L2[0], L2[1]);
3631 } else if (CPU == CPU_65C02 &&
3634 !RegAUsed (L2[1])) {
3636 /* Replace by trb */
3637 ReplaceLine (L, "\tlda\t#$%02X", (~Val) & 0xFF);
3638 ReplaceLine (L2[0], "\ttrb\t%s", L2[1]->Line+5);
3645 L = ReplaceLine (L, "\tlda\t#$%02X", Val);
3646 ReplaceLine (L2[0], "\tand\t%s", L2[1]->Line+5);
3651 } else if (LineMatch (L2[0], "\tora\t#$")) {
3653 unsigned Val = GetHexNum (L2[0]->Line+7);
3656 /* ORA with 0x00, just load the value from memory */
3657 FreeLines (L2[0], L2[1]);
3659 } else if (Val == 0xFF) {
3661 /* ORA with 0xFF, replace by a store of $FF */
3663 ReplaceLine (L2[0], "\tlda\t#$FF");
3665 } else if (CPU == CPU_65C02 &&
3668 !RegAUsed (L2[1])) {
3670 /* Replace by trb */
3671 ReplaceLine (L, "\tlda\t#$%02X", Val);
3672 ReplaceLine (L2[0], "\ttsb\t%s", L2[1]->Line+5);
3679 L = ReplaceLine (L, "\tlda\t#$%02X", Val);
3680 ReplaceLine (L2[0], "\tora\t%s", L2[1]->Line+5);
3685 } else if (LineMatch (L2[0], "\teor\t#$") ||
3686 LineMatch (L2[0], "\tadc\t#$")) {
3689 L = ReplaceLine (L, "\tlda\t%s", L2[0]->Line+5);
3690 ReplaceLine (L2[0], "\t%.3s\t%s", L2[0]->Line+1, L2[1]->Line+5);
3696 L = NextCodeLine (L);
3702 static void OptNeg (void)
3703 /* Optimize the "bnegax/jeq" and "bnegax/jne" sequences */
3707 Line* L = FirstCode;
3710 /* Search for the sequence:
3716 * and replace it by:
3721 if (LineMatch (L, "\tlda\t") && /* Match on start */
3722 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
3723 LineFullMatch (L2 [0], "\tjsr\tbnega") &&
3724 IsCondJump (L2 [1])) {
3726 /* Found the sequence, replace it */
3728 InvertZJump (L2 [1]);
3732 /* Search for the sequence:
3750 else if (LineMatch (L, "\tldy\t#$") &&
3751 GetNextCodeLines (L, L2, 6) &&
3752 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
3753 LineFullMatch (L2[1], "\ttax") &&
3754 LineFullMatch (L2[2], "\tdey") &&
3755 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
3756 LineFullMatch (L2[4], "\tjsr\tbnegax") &&
3757 IsCondJump (L2[5])) {
3759 L2[1] = ReplaceLine (L2[1], "\tdey");
3760 L2[2] = ReplaceLine (L2[2], "\tora\t(sp),y");
3761 FreeLines (L2[3], L2[4]);
3762 InvertZJump (L2[5]);
3766 /* Search for the sequence:
3779 else if (LineMatch (L, "\tlda\t") &&
3780 GetNextCodeLines (L, L2, 3) &&
3781 IsLoadAX (L, L2[0]) &&
3782 LineFullMatch (L2[1], "\tjsr\tbnegax") &&
3783 IsCondJump (L2[2])) {
3785 /* Replace the load of X by ora */
3786 L2[0]->Line[1] = 'o';
3787 L2[0]->Line[2] = 'r';
3788 L2[0]->Line[3] = 'a';
3790 InvertZJump (L2[2]);
3794 /* Search for the sequence:
3800 * and replace it by:
3806 else if (LineMatch (L, "\tjsr\t_") && /* Match on start */
3807 GetNextCodeLines (L, L2, 2) &&
3808 LineMatch (L2 [0], "\tjsr\tbnega") &&
3809 IsCondJump (L2 [1])) {
3811 if (LineFullMatch (L2 [0], "\tjsr\tbnega")) {
3813 L2 [0] = ReplaceLine (L2 [0], "\ttax"); /* Test a */
3816 L2 [0] = ReplaceLine (L2 [0], "\tstx\ttmp1");
3817 NewLineAfter (L2 [0], "\tora\ttmp1");
3820 /* Invert the jump */
3821 InvertZJump (L2 [1]);
3826 L = NextCodeLine (L);
3832 static void OptTriples (void)
3833 /* Replace code triples */
3835 static const char* Pat1 [] = {
3843 static const char* Pat2 [] = {
3851 static const char* Replace [] = {
3859 Line* L = FirstCode;
3861 int I = LineFullMatchX (L, Pat1);
3863 /* We found the first match, get the next line */
3864 Line* L2 = NextCodeLine (L);
3865 if (L2 && LineFullMatch (L2, Pat2 [I])) {
3866 /* Found. Replace by the short call */
3868 L = ReplaceLine (L, Replace [I]);
3872 L = NextCodeLine (L);
3878 static Line* OptOneBlock (Line* L)
3879 /* Optimize the register contents inside one basic block */
3881 static const char* Compares [] = {
3882 "\tjsr\ttoseq00", "\tjsr\ttoseqa0", "\tjsr\ttoseqax",
3883 "\tjsr\ttoseqeax", "\tjsr\ttosne00", "\tjsr\ttosnea0",
3884 "\tjsr\ttosneax", "\tjsr\ttosneeax", "\tjsr\ttoslt00",
3885 "\tjsr\ttoslta0", "\tjsr\ttosltax", "\tjsr\ttosult00",
3886 "\tjsr\ttosulta0", "\tjsr\ttosultax", "\tjsr\ttoslteax",
3887 "\tjsr\ttosulteax", "\tjsr\ttosle00", "\tjsr\ttoslea0",
3888 "\tjsr\ttosleax", "\tjsr\ttosule00", "\tjsr\ttosulea0",
3889 "\tjsr\ttosuleax", "\tjsr\ttosleeax", "\tjsr\ttosuleeax",
3890 "\tjsr\ttosgt00", "\tjsr\ttosgta0", "\tjsr\ttosgtax",
3891 "\tjsr\ttosugt00", "\tjsr\ttosugta0", "\tjsr\ttosugtax",
3892 "\tjsr\ttosgteax", "\tjsr\ttosugteax", "\tjsr\ttosge00",
3893 "\tjsr\ttosgea0", "\tjsr\ttosgeax", "\tjsr\ttosuge00",
3894 "\tjsr\ttosugea0", "\tjsr\ttosugeax", "\tjsr\ttosgeeax",
3899 static const char* MakeBool [] = {
3900 "\tjsr\tbooleq", "\tjsr\tboolne", "\tjsr\tboollt",
3901 "\tjsr\tboolle", "\tjsr\tboolgt", "\tjsr\tboolge",
3902 "\tjsr\tboolult", "\tjsr\tboolule", "\tjsr\tboolugt",
3907 int A = -1; /* Contents of A register */
3908 int X = -1; /* Contents of X register */
3909 int Y = -1; /* Contents of Y register */
3914 while (L && !IsLabel (L)) {
3916 /* Handle all instructions. All instructions not tested here have
3917 * no effects on the register contents.
3920 if (L->Line [0] == '+') {
3921 /* This is a hint */
3922 if (LineMatch (L, "+a:")) {
3923 /* Information about a */
3924 switch (L->Line [3]) {
3925 case '!': A = -1; break;
3926 case '=': A = GetHexNum (L->Line + 4); break;
3928 } else if (LineMatch (L, "+x:")) {
3929 /* The code generator tells something about the x register */
3930 switch (L->Line [3]) {
3931 case '!': X = -1; break;
3932 case '=': X = GetHexNum (L->Line + 4); break;
3934 } else if (LineMatch (L, "+y:")) {
3935 /* Information about the y register */
3936 switch (L->Line [3]) {
3937 case '!': Y = -1; break;
3938 case '=': Y = GetHexNum (L->Line + 4); break;
3941 } else if (LineMatch (L, "\tadc\t")) {
3942 if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
3943 L->Line[strlen(L->Line)-2] = '\0';
3946 } else if (LineMatch (L, "\tand\t")) {
3948 } else if (LineFullMatch (L, "\tasl\ta")) {
3950 A = (A << 1) & 0xFF;
3952 } else if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
3953 L->Line[strlen(L->Line)-2] = '\0';
3954 } else if (CPU == CPU_65C02 && (LineFullMatch (L, "\tdea") ||
3955 LineFullMatch (L, "\tdec\ta"))) {
3957 } else if (LineFullMatch (L, "\tdex")) {
3959 } else if (LineFullMatch (L, "\tdey")) {
3961 } else if (LineMatch (L, "\teor")) {
3963 } else if (CPU == CPU_65C02 && (LineFullMatch (L, "\tina") ||
3964 LineFullMatch (L, "\tinc\ta"))) {
3966 } else if (LineFullMatch (L, "\tinx")) {
3968 } else if (LineFullMatch (L, "\tiny")) {
3970 } else if (LineFullMatch (L, "\tjsr\taddeq0sp")) {
3971 /* We know about this function */
3974 } else if (LineFullMatch (L, "\tjsr\taddeqysp")) {
3975 /* We know about this function */
3978 } else if (LineFullMatch (L, "\tjsr\taxulong")) {
3979 /* We know about this function and we're trying to replace it by
3980 * inline code if we have already a register that contains zero.
3985 } else if (X == 0) {
3987 } else if (Y == 0) {
3993 /* We cannot replace the code, but we know about the results */
3996 L = ReplaceLine (L, "\tst%c\tsreg", C);
3997 NewLineAfter (L, "\tst%c\tsreg+1", C);
3999 } else if (LineFullMatch (L, "\tjsr\tbnega")) {
4000 /* We know about this function */
4003 } else if (LineFullMatch (L, "\tjsr\tbnegax")) {
4004 /* We know about this function */
4007 } else if (LineFullMatch (L, "\tjsr\tbnegeax")) {
4008 /* We know about this function */
4011 } else if (LineFullMatch (L, "\tjsr\tcomplax")) {
4012 /* We know about this function */
4019 } else if (LineFullMatch (L, "\tjsr\tdecax1")) {
4020 /* We know about this function */
4022 } else if (LineFullMatch (L, "\tjsr\tdecax2")) {
4023 /* We know about this function */
4025 } else if (LineFullMatch (L, "\tjsr\tdecaxy")) {
4026 /* We know about this function */
4028 } else if (LineFullMatch (L, "\tjsr\tdeceaxy")) {
4029 /* We know about this function */
4031 } else if (LineFullMatch (L, "\tjsr\tincax1")) {
4032 /* We know about this function */
4034 } else if (LineFullMatch (L, "\tjsr\tincax2")) {
4035 /* We know about this function */
4037 } else if (LineFullMatch (L, "\tjsr\tinceaxy")) {
4038 /* We know about this function */
4040 } else if (LineFullMatch (L, "\tjsr\tladdeq")) {
4041 /* We know about this function */
4044 } else if (LineFullMatch (L, "\tjsr\tladdeqb")) {
4045 /* We know about this function */
4048 } else if (LineFullMatch (L, "\tjsr\tlbneg")) {
4049 /* We know about this function */
4052 } else if (LineFullMatch (L, "\tjsr\tldai")) {
4053 /* We know about this function */
4056 } else if (LineFullMatch (L, "\tjsr\tldaidx")) {
4057 /* We know about this function */
4059 } else if (LineFullMatch (L, "\tjsr\tldau00sp")) {
4060 /* We know about this function */
4064 } else if (LineFullMatch (L, "\tjsr\tldau0ysp")) {
4065 /* We know about this function */
4069 } else if (LineFullMatch (L, "\tjsr\tldaui")) {
4070 /* We know about this function */
4074 } else if (LineFullMatch (L, "\tjsr\tldaui0sp")) {
4078 } else if (LineFullMatch (L, "\tjsr\tldauidx")) {
4079 /* We know about this function */
4082 } else if (LineFullMatch (L, "\tjsr\tldauiysp")) {
4083 /* We know about this function */
4087 } else if (LineFullMatch (L, "\tjsr\tldax0sp")) {
4088 /* We know about this function */
4091 } else if (LineFullMatch (L, "\tjsr\tldaxi")) {
4092 /* We know about this function */
4095 } else if (LineFullMatch (L, "\tjsr\tldaxidx")) {
4096 /* We know about this function */
4099 } else if (LineFullMatch (L, "\tjsr\tldaxysp")) {
4100 /* We know about this function */
4103 } else if (LineFullMatch (L, "\tjsr\tldeaxi")) {
4104 /* We know about this function */
4107 } else if (LineFullMatch (L, "\tjsr\tldeaxidx")) {
4108 /* We know about this function */
4111 } else if (LineFullMatch (L, "\tjsr\tlsubeq")) {
4112 /* We know about this function */
4115 } else if (LineFullMatch (L, "\tjsr\tlsubeqb")) {
4116 /* We know about this function */
4119 } else if (LineFullMatch (L, "\tjsr\tnegax")) {
4120 /* We know about this function */
4122 } else if (LineFullMatch (L, "\tjsr\tnegeax")) {
4123 /* We know about this function */
4125 } else if (LineFullMatch (L, "\tjsr\tpush0")) {
4126 /* We know about this function */
4130 } else if (LineFullMatch (L, "\tjsr\tpush1")) {
4131 /* We know about this function */
4135 } else if (LineFullMatch (L, "\tjsr\tpush2")) {
4136 /* We know about this function */
4140 } else if (LineFullMatch (L, "\tjsr\tpush3")) {
4141 /* We know about this function */
4145 } else if (LineFullMatch (L, "\tjsr\tpush4")) {
4146 /* We know about this function */
4150 } else if (LineFullMatch (L, "\tjsr\tpush5")) {
4151 /* We know about this function */
4155 } else if (LineFullMatch (L, "\tjsr\tpush6")) {
4156 /* We know about this function */
4160 } else if (LineFullMatch (L, "\tjsr\tpush7")) {
4161 /* We know about this function */
4165 } else if (CPU == CPU_65C02 && LineFullMatch (L, "\tjsr\tpusha")) {
4166 /* We know about this function */
4168 } else if (LineFullMatch (L, "\tjsr\tpusha0")) {
4169 /* We know about this function
4170 * If X is already zero, we may call pushax instead and save two
4174 L = ReplaceLine (L, "\tjsr\tpushax");
4178 } else if (LineFullMatch (L, "\tjsr\tpushax")) {
4179 /* We know about this function */
4181 } else if (LineFullMatch (L, "\tjsr\tpushaysp")) {
4182 /* We know about this function */
4185 } else if (LineFullMatch (L, "\tjsr\tpushc0")) {
4186 /* We know about this function */
4189 } else if (LineFullMatch (L, "\tjsr\tpushc1")) {
4190 /* We know about this function */
4193 } else if (LineFullMatch (L, "\tjsr\tpushc2")) {
4194 /* We know about this function */
4197 } else if (LineFullMatch (L, "\tjsr\tpushw")) {
4198 /* We know about this function (calls pushax) */
4201 } else if (LineFullMatch (L, "\tjsr\tpushw0sp")) {
4202 /* We know about this function(calls pushax) */
4205 } else if (LineFullMatch (L, "\tjsr\tpushwidx")) {
4206 /* We know about this function (calls pushax) */
4209 } else if (LineFullMatch (L, "\tjsr\tpushwysp")) {
4210 /* We know about this function (calls pushax) */
4213 } else if (LineFullMatch (L, "\tjsr\tresteax")) {
4214 /* We know about this function */
4216 } else if (LineFullMatch (L, "\tjsr\tsaveeax")) {
4217 /* We know about this function */
4218 /* Changes nothing */
4219 } else if (LineFullMatch (L, "\tjsr\tshrax1")) {
4220 /* We know about this function */
4222 } else if (LineFullMatch (L, "\tjsr\tshrax2")) {
4223 /* We know about this function */
4225 } else if (LineFullMatch (L, "\tjsr\tshrax3")) {
4226 /* We know about this function */
4228 } else if (LineFullMatch (L, "\tjsr\tshreax1")) {
4229 /* We know about this function */
4231 } else if (LineFullMatch (L, "\tjsr\tshreax2")) {
4232 /* We know about this function */
4234 } else if (LineFullMatch (L, "\tjsr\tshreax3")) {
4235 /* We know about this function */
4237 } else if (LineFullMatch (L, "\tjsr\tstaspp")) {
4238 /* We know about this function */
4240 } else if (LineFullMatch (L, "\tjsr\tstaxspp")) {
4241 /* We know about this function */
4243 } else if (LineFullMatch (L, "\tjsr\tstax0sp")) {
4244 /* We know about this function */
4246 } else if (LineFullMatch (L, "\tjsr\tstaxysp")) {
4247 /* We know about this function */
4249 } else if (LineFullMatch (L, "\tjsr\tsubeq0sp")) {
4250 /* We know about this function */
4253 } else if (LineFullMatch (L, "\tjsr\tsubeqysp")) {
4254 /* We know about this function */
4257 } else if (LineFullMatch (L, "\tjsr\ttosadda0")) {
4258 /* We know about this function */
4261 } else if (LineFullMatch (L, "\tjsr\ttosaddax")) {
4262 /* We know about this function */
4265 } else if (LineFullMatch (L, "\tjsr\ttosicmp")) {
4266 /* We know about this function */
4269 } else if (LineFullMatchX (L, Compares) >= 0) {
4272 } else if (LineFullMatchX (L, MakeBool) >= 0) {
4275 } else if (LineMatch (L, "\tjsr\t")) {
4276 /* Subroutine call, forget all register information */
4278 } else if (LineMatch (L, "\tlda\t")) {
4279 if (!RegAUsed (L) && !IsCondJump (NextInstruction (L))) {
4280 /* The value loaded is not used later, remove it */
4282 } else if (LineMatch (L, "\tlda\t(")) {
4283 if (IsXAddrMode (L)) {
4285 /* If X is zero and we have a 65C02 cpu, replace it by
4288 if (X == 0 && CPU == CPU_65C02) {
4289 unsigned Len = strlen (L->Line);
4290 L->Line [Len-3] = ')';
4291 L->Line [Len-2] = '\0';
4292 /* If Y and X are both zero, replace by load indirect
4293 * y and save one cycle in some cases.
4295 } else if (X == 0 && Y == 0) {
4297 const char* S = L->Line + 6;
4299 strcpy (Buf, "\tlda\t(");
4307 L = ReplaceLine (L, Buf);
4309 } else if (IsYAddrMode (L)) {
4310 /* lda (zp),y. If Y is zero and we have a 65C02 CPU,
4311 * replace it by an indirect load.
4313 if (Y == 0 && CPU == CPU_65C02) {
4314 unsigned Len = strlen (L->Line);
4315 L->Line [Len-3] = ')';
4316 L->Line [Len-2] = '\0';
4319 /* In any case invalidate A */
4321 } else if (LineMatch (L, "\tlda\t#$")) {
4322 /* Immidiate load into A */
4323 NewVal = GetHexNum (L->Line + 7);
4325 /* Load has no effect */
4327 } else if (NewVal == X) {
4328 /* Requested value is already in X */
4329 L = ReplaceLine (L, "\ttxa");
4330 } else if (NewVal == Y) {
4331 /* Requested value is already in Y */
4332 L = ReplaceLine (L, "\ttya");
4333 } else if (CPU == CPU_65C02 && A != -1) {
4334 /* Try ina/dea operators of 65C02 */
4335 if (NewVal == ((A - 1) & 0xFF)) {
4336 L = ReplaceLine (L, "\tdea");
4337 } else if (NewVal == ((A + 1) & 0xFF)) {
4338 L = ReplaceLine (L, "\tina");
4341 /* Anyway, the new value is now in A */
4344 /* Memory load into A */
4347 } else if (LineMatch (L, "\tldax\t")) {
4348 /* Memory load into A and X */
4350 } else if (LineMatch (L, "\tldx\t")) {
4351 if (!RegXUsed (L) && !IsCondJump (NextInstruction (L))) {
4352 /* The value loaded is not used later, remove it */
4354 } else if (LineMatch (L, "\tldx\t#$")) {
4355 /* Immidiate load into X */
4356 NewVal = GetHexNum (L->Line + 7);
4358 /* Load has no effect */
4360 } else if (NewVal == A) {
4361 /* Requested value is already in A */
4362 L = ReplaceLine (L, "\ttax");
4363 } else if (X != -1 && NewVal == ((X + 1) & 0xFF)) {
4364 /* Requested value is one more than current contents */
4365 L = ReplaceLine (L, "\tinx");
4366 } else if (X != -1 && NewVal == ((X - 1) & 0xFF)) {
4367 /* Requested value is one less than current contents */
4368 L = ReplaceLine (L, "\tdex");
4370 /* Anyway, the new value is now in X */
4373 /* Memory load into X */
4376 } else if (LineMatch (L, "\tldy\t")) {
4377 if (!RegYUsed (L) && !IsCondJump (NextInstruction (L))) {
4378 /* The value loaded is not used later, remove it */
4380 } else if (LineMatch (L, "\tldy\t#$")) {
4381 /* Immidiate load into Y */
4382 NewVal = GetHexNum (L->Line + 7);
4384 /* Load has no effect */
4386 } else if (NewVal == A) {
4387 /* Requested value is already in A */
4388 L = ReplaceLine (L, "\ttay");
4389 } else if (Y != -1 && NewVal == ((Y + 1) & 0xFF)) {
4390 /* Requested value is one more than current contents */
4391 L = ReplaceLine (L, "\tiny");
4392 } else if (Y != -1 && NewVal == ((Y - 1) & 0xFF)) {
4393 /* Requested value is one less than current contents */
4394 L = ReplaceLine (L, "\tdey");
4396 /* Anyway, the new value is now in Y */
4399 /* Memory load into Y */
4402 } else if (LineFullMatch (L, "\tlsr\ta")) {
4406 } else if (LineMatch (L, "\tora\t#$")) {
4408 A |= GetHexNum (L->Line + 7);
4410 } else if (LineMatch (L, "\tora\t")) {
4412 } else if (LineFullMatch (L, "\tpla")) {
4414 } else if (LineFullMatch (L, "\trol\ta")) {
4416 } else if (LineFullMatch (L, "\tror\ta")) {
4418 } else if (LineFullMatch (L, "\trts")) {
4420 } else if (LineFullMatch (L, "\trti")) {
4422 } else if (LineMatch (L, "\tsbc\t")) {
4423 if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
4424 L->Line[strlen(L->Line)-2] = '\0';
4427 } else if (CPU == CPU_65C02 && LineMatch (L, "\tst")) {
4428 /* Try to replace by stz if possible */
4429 if (A == 0 && LineMatch (L, "\tsta\t")) {
4430 /* Not indirect and not Y allowed */
4431 if (L->Line[5] != '(' && !IsYAddrMode (L)) {
4434 } else if (X == 0 && LineMatch (L, "\tstx\t")) {
4435 /* absolute,y not allowed */
4436 if (!IsYAddrMode (L)) {
4439 } else if (Y == 0 && LineMatch (L, "\tsty\t")) {
4440 /* sty and stz share all addressing modes */
4443 } else if (LineFullMatch (L, "\ttax")) {
4444 if (A != -1 && X == A) {
4445 /* Load has no effect */
4450 } else if (LineFullMatch (L, "\ttay")) {
4451 if (A != -1 && Y == A) {
4452 /* Load has no effect */
4457 } else if (LineFullMatch (L, "\ttsx")) {
4459 } else if (LineFullMatch (L, "\ttxa")) {
4460 if (X != -1 && A == X) {
4461 /* Load has no effect */
4466 } else if (LineFullMatch (L, "\ttya")) {
4467 if (Y != -1 && A == Y) {
4468 /* Load has no effect */
4475 /* Set to next line, handle deletions */
4476 L2 = NextCodeSegLine (L);
4484 /* Skip the label */
4485 L = NextCodeSegLine (L);
4492 static void OptBlocks (void)
4493 /* Optimize the register contents inside basic blocks */
4495 Line* L = FirstCode;
4497 L = OptOneBlock (L);
4503 static void OptJumps (void)
4504 /* Optimize jumps */
4506 static const char* Jumps [] = {
4516 Line* L = FirstCode;
4518 int I = LineMatchX (L, Jumps);
4520 Line* Target = GetTargetLine (L->Line+5);
4521 if (Target->Index > L->Index) {
4522 /* This is a forward jump. Backward jumps are handled
4523 * automagically by the assembler.
4525 unsigned Distance = GetJumpDistance (L, Target);
4526 if (Distance < 123) { /* Safety */
4527 L->Line [1] = 'b'; /* Make a short branch */
4528 L->Size = 2; /* Set new size */
4532 L = NextCodeLine (L);
4535 /* Special treatment for jumps on the 65C02 */
4536 if (CPU == CPU_65C02) {
4538 Line* L = FirstCode;
4540 if (LineMatch (L, "\tjmp\tL")) {
4541 Line* Target = GetTargetLine (L->Line+5);
4542 unsigned Distance = GetJumpDistance (L, Target);
4543 if (Distance < 123) { /* Safety */
4544 L->Line [1] = 'b'; /* Make a short branch */
4547 L->Size = 2; /* Set new size */
4550 L = NextCodeLine (L);
4558 static void OptRTS (void)
4559 /* Change sequences of jsr XXX/rts to jmp XXX */
4561 Line* L = FirstCode;
4563 if (LineMatch (L, "\tjsr\t")) {
4564 /* This is a jsr, get the next instruction */
4565 Line* L2 = NextCodeLine (L);
4566 if (L2 && LineFullMatch (L2, "\trts")) {
4567 /* We found a sequence */
4573 /* Try the next line */
4574 L = NextCodeLine (L);
4580 /*****************************************************************************/
4582 /*****************************************************************************/
4586 void OptDoOpt (void)
4587 /* Run the optimizer over the collected stuff */
4589 typedef void (*OptFunc)(void);
4591 /* Table with optimizer steps - are called in this order */
4592 static const OptFunc OptFuncs [] = {
4593 OptCompares1, /* Optimize compares - first step */
4594 OptDeadJumps, /* Remove dead jumps */
4595 OptLoads, /* Remove unnecessary loads */
4596 OptRegLoads, /* Remove unnecessary register loads */
4597 OptPtrOps, /* Optimize stores through pointers */
4598 OptRegVars, /* Optimize use of register variables */
4599 OptDoubleJumps, /* Remove jump cascades - must be used before OptNeg */
4600 OptNeg, /* Remove unnecessary boolean negates */
4601 OptJumpRTS, /* Replace jumps to an RTS by an RTS */
4602 OptBoolTransforms, /* Optimize boolean transforms */
4603 OptCompares2, /* Optimize compares */
4604 OptTests, /* Remove unnecessary tests */
4605 OptBitOps, /* Optimize bit operations */
4606 OptTriples, /* Optimize several triples */
4607 OptBlocks, /* Optimize basic blocks */
4608 OptRegLoads, /* Remove unnecessary register loads (another pass) */
4609 OptBlocks, /* Optimize basic blocks */
4610 OptJumps, /* Optimize jumps */
4611 OptRTS, /* Optimize jsr/rts sequences */
4614 unsigned long Flags;
4617 /* Find and remember the first line of code */
4620 /* Mark all lines inside the code segment */
4623 /* Create a list of all local labels for fast access */
4626 /* Ok, now start the real optimizations */
4628 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I, Flags <<= 1) {
4629 if ((OptDisable & Flags) == 0) {
4631 } else if (Verbose || Debug) {
4632 printf ("Optimizer pass %u skipped\n", I);