1 /*****************************************************************************/
5 /* An optimizer for the cc65 C compiler */
9 /* (C) 1998 Ullrich von Bassewitz */
11 /* D-70597 Stuttgart */
12 /* EMail: uz@musoftware.de */
15 /* This software is provided 'as-is', without any expressed or implied */
16 /* warranty. In no event will the authors be held liable for any damages */
17 /* arising from the use of this software. */
19 /* Permission is granted to anyone to use this software for any purpose, */
20 /* including commercial applications, and to alter it and redistribute it */
21 /* freely, subject to the following restrictions: */
23 /* 1. The origin of this software must not be misrepresented; you must not */
24 /* claim that you wrote the original software. If you use this software */
25 /* in a product, an acknowledgment in the product documentation would be */
26 /* appreciated but is not required. */
27 /* 2. Altered source versions must be plainly marked as such, and must not */
28 /* be misrepresented as being the original software. */
29 /* 3. This notice may not be removed or altered from any source */
32 /*****************************************************************************/
58 /*****************************************************************************/
60 /*****************************************************************************/
64 /* Bitset of flags that switch the different optimizer passes */
65 unsigned long OptDisable = 0;
69 /* Bitmapped flags for the Flags field in the Line struct */
70 #define OF_CODE 0x0001 /* This line is in a code segment */
72 /* Pointer to first code line */
73 static Line* FirstCode;
76 static Line** Labels = 0; /* Pointers to label lines */
77 static unsigned LabelCount = 0; /* Count of local labels found */
79 /* A collection of lines */
80 typedef struct LineColl_ LineColl;
82 unsigned Count; /* Count of lines in the collection */
83 unsigned Max; /* Maximum count of lines */
84 Line* Lines[1]; /* Lines, dynamically allocated */
89 /* Calculate the element count of a table */
90 #define COUNT(T) (sizeof (T) / sizeof (T [0]))
92 /* Macro to increment and decrement register contents if they're valid */
93 #define INC(reg,val) if ((reg) >= 0) (reg) = ((reg) + val) & 0xFF
94 #define DEC(reg,val) if ((reg) >= 0) (reg) = ((reg) - val) & 0xFF
96 /* Defines for the conditions in a compare */
108 /* Defines for registers */
109 #define REG_NONE 0x00
113 #define REG_AX (REG_A | REG_X)
114 #define REG_ALL (REG_A | REG_X | REG_Y)
116 /* Description of the commands */
117 static const struct {
118 const char* Insn; /* Instruction */
119 unsigned char FullMatch; /* Match full instuction? */
120 unsigned char Use; /* Registers used */
121 unsigned char Load; /* Registers loaded */
123 { "\tadc\t", 0, REG_A, REG_NONE },
124 { "\tand\t", 0, REG_A, REG_NONE },
125 { "\tasl\ta", 1, REG_A, REG_NONE },
126 { "\tasl\t", 0, REG_NONE, REG_NONE },
127 { "\tclc", 1, REG_NONE, REG_NONE },
128 { "\tcld", 1, REG_NONE, REG_NONE },
129 { "\tcli", 1, REG_NONE, REG_NONE },
130 { "\tcmp\t", 0, REG_A, REG_NONE },
131 { "\tcpx\t", 0, REG_X, REG_NONE },
132 { "\tcpy\t", 0, REG_Y, REG_NONE },
133 { "\tdea", 1, REG_A, REG_NONE },
134 { "\tdec\ta", 1, REG_A, REG_NONE },
135 { "\tdec\t", 0, REG_NONE, REG_NONE },
136 { "\tdex", 1, REG_X, REG_NONE },
137 { "\tdey", 1, REG_Y, REG_NONE },
138 { "\teor\t", 0, REG_A, REG_NONE },
139 { "\tina", 1, REG_A, REG_NONE },
140 { "\tinc\ta", 1, REG_A, REG_NONE },
141 { "\tinc\t", 0, REG_NONE, REG_NONE },
142 { "\tinx", 1, REG_X, REG_NONE },
143 { "\tiny", 1, REG_Y, REG_NONE },
144 { "\tjsr\tbool", 0, REG_NONE, REG_AX },
145 { "\tjsr\tdecaxy", 1, REG_ALL, REG_AX },
146 { "\tjsr\tdecax", 0, REG_AX, REG_AX },
147 { "\tjsr\tldax0sp", 1, REG_Y, REG_AX },
148 { "\tjsr\tldaxysp", 1, REG_Y, REG_AX },
149 { "\tjsr\tpusha", 1, REG_A, REG_Y },
150 { "\tjsr\tpusha0", 1, REG_A, REG_X | REG_Y },
151 { "\tjsr\tpushax", 1, REG_AX, REG_Y },
152 { "\tjsr\tpushw0sp", 1, REG_NONE, REG_ALL },
153 { "\tjsr\tpushwysp", 1, REG_Y, REG_ALL },
154 { "\tjsr\ttosicmp", 1, REG_AX, REG_ALL },
155 { "\tlda\t", 0, REG_NONE, REG_A },
156 { "\tldax\t", 0, REG_NONE, REG_AX },
157 { "\tldx\t", 0, REG_NONE, REG_X },
158 { "\tldy\t", 0, REG_NONE, REG_Y },
159 { "\tlsr\ta", 1, REG_A, REG_NONE },
160 { "\tlsr\t", 0, REG_NONE, REG_NONE },
161 { "\tnop", 1, REG_NONE, REG_NONE },
162 { "\tora\t", 0, REG_A, REG_NONE },
163 { "\tpha", 1, REG_A, REG_NONE },
164 { "\tphp", 1, REG_NONE, REG_NONE },
165 { "\tpla", 1, REG_NONE, REG_A },
166 { "\tplp", 1, REG_NONE, REG_NONE },
167 { "\trol\ta", 1, REG_A, REG_A },
168 { "\trol\t", 0, REG_NONE, REG_NONE },
169 { "\tror\ta", 1, REG_A, REG_A },
170 { "\tror\t", 0, REG_NONE, REG_NONE },
171 { "\tsbc\t", 0, REG_A, REG_NONE },
172 { "\tsec", 1, REG_NONE, REG_NONE },
173 { "\tsed", 1, REG_NONE, REG_NONE },
174 { "\tsei", 1, REG_NONE, REG_NONE },
175 { "\tsta\t", 0, REG_A, REG_NONE },
176 { "\tstx\t", 0, REG_X, REG_NONE },
177 { "\tsty\t", 0, REG_Y, REG_NONE },
178 { "\tstz\t", 0, REG_NONE, REG_NONE },
179 { "\ttax", 1, REG_A, REG_X },
180 { "\ttay", 1, REG_A, REG_Y },
181 { "\ttrb\t", 0, REG_A, REG_NONE },
182 { "\ttsb\t", 0, REG_A, REG_NONE },
183 { "\ttsx", 1, REG_NONE, REG_X },
184 { "\ttxa", 1, REG_X, REG_A },
185 { "\ttya", 1, REG_Y, REG_A },
190 /* Table with the compare suffixes */
191 static const char CmpSuffixTab [][4] = {
192 "eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule"
195 /* Table used to invert a condition, indexed by condition */
196 static const unsigned char CmpInvertTab [] = {
198 CMP_LE, CMP_LT, CMP_GE, CMP_GT,
199 CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT
202 /* Table to show which compares are signed (use the N flag) */
203 static const char CmpSignedTab [] = {
204 0, 0, 1, 1, 1, 1, 0, 0, 0, 0
209 /* Lists of branches */
210 static const char* ShortBranches [] = {
221 static const char* LongBranches [] = {
235 /*****************************************************************************/
237 /*****************************************************************************/
241 static unsigned EstimateSize (Line* L);
242 /* Estimate the size of an instruction */
244 static int IsLocalLabel (const Line* L);
245 /* Return true if the line is a local label line */
247 static unsigned GetLabelNum (const char* L);
248 /* Return the label number of a label line */
250 static unsigned RVUInt1 (Line* L, LineColl* LC, unsigned Used, unsigned Unused);
251 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
253 static Line* NewLineAfter (Line* LineBefore, const char* Format, ...) attribute ((format(printf,2,3)));
254 /* Create a new line, insert it after L and return it. The new line is marked
258 static Line* ReplaceLine (Line* L, const char* Format, ...)
259 attribute ((format(printf,2,3)));
260 /* Replace one line by another */
264 /*****************************************************************************/
266 /*****************************************************************************/
270 static Line* NewLineAfter (Line* LineBefore, const char* Format, ...)
271 /* Create a new line, insert it after L and return it. The new line is marked
277 /* Format the new line and add it */
279 va_start (ap, Format);
280 L = NewCodeLineAfter (LineBefore, Format, ap);
283 /* Make the line a code line */
286 /* Estimate the code size */
287 L->Size = EstimateSize (L);
289 /* Return the new line */
295 static Line* NewLabelAfter (Line* L, unsigned Label)
296 /* Add a new line with a definition of a local label after the line L */
300 /* Create the label */
301 sprintf (Buf, "L%04X:", Label);
303 /* Create a new line */
304 L = NewLineAfter (L, Buf);
306 /* Insert this label into the label list */
309 /* Return the new line */
315 static void FreeLine (Line* L)
316 /* Remove a line from the list and free it */
318 /* If this is a label line, remove it from the label list */
319 if (IsLocalLabel (L)) {
320 Labels [GetLabelNum (L->Line)] = 0;
323 /* Unlink the line */
329 static Line* ReplaceLine (Line* L, const char* Format, ...)
330 /* Replace one line by another */
335 /* Format the new line */
337 va_start (ap, Format);
338 xvsprintf (S, sizeof (S), Format, ap);
341 /* Get the length of the new line */
344 /* We can copy the line if the old line has space enough */
347 /* Just copy the new line, but don't update the length */
348 memcpy (L->Line, S, Len);
349 L->Line [Len] = '\0';
353 /* We must allocate new space */
354 Line* NewLine = xmalloc (sizeof (Line) + Len);
356 /* Set the values in the new struct */
357 NewLine->Flags = L->Flags;
358 NewLine->Index = L->Index;
359 NewLine->Size = L->Size; /* Hmm ... */
361 memcpy (NewLine->Line, S, Len + 1);
363 /* Replace the old struct in the list */
364 NewLine->Next = L->Next;
366 NewLine->Next->Prev = NewLine;
371 NewLine->Prev = L->Prev;
373 NewLine->Prev->Next = NewLine;
379 /* Free the old struct */
384 /* Estimate the new size */
385 if (L->Flags & OF_CODE) {
386 L->Size = EstimateSize (L);
389 /* Return the line */
395 static Line* PrevCodeLine (Line* L)
396 /* Return the previous line containing code */
400 if (L->Flags & OF_CODE && L->Line [0] != '+') {
410 static Line* NextCodeSegLine (Line* L)
411 /* Return the next line in the code segment */
415 if (L->Flags & OF_CODE) {
425 static Line* NextCodeLine (Line* L)
426 /* Return the next line containing code */
430 if ((L->Flags & OF_CODE) != 0 && L->Line [0] != '+') {
440 static Line* NextInstruction (Line* L)
441 /* Return the next line containing code, ignoring labels. */
444 L = NextCodeLine (L);
445 } while (L && (L->Line[0] == '+' || IsLocalLabel(L)));
451 static void FreeLines (Line* Start, Line* End)
452 /* Delete all lines from Start to End, both inclusive */
457 Start = NextCodeSegLine (Start);
464 /*****************************************************************************/
465 /* Line Collections */
466 /*****************************************************************************/
470 static LineColl* NewLineColl (unsigned Size)
471 /* Create a new line collection and return it */
473 /* Allocate memory */
474 LineColl* LC = xmalloc (sizeof (LineColl) + sizeof (Line) * (Size-1));
476 /* Initialize members */
480 /* Return the new collection */
486 static void FreeLineColl (LineColl* LC)
487 /* Delete a line collection */
494 static int LCAddLine (LineColl* LC, Line* L)
495 /* Add a line. Return 0 if no space available, return 1 otherwise */
497 /* Check if there is enough space available */
498 if (LC->Count >= LC->Max) {
499 /* No room available */
504 LC->Lines [LC->Count++] = L;
512 static int LCHasLine (LineColl* LC, Line* L)
513 /* Check if the given line is in the collection */
516 for (I = 0; I < LC->Count; ++I) {
517 if (LC->Lines[I] == L) {
526 /*****************************************************************************/
527 /* Test a line for several things */
528 /*****************************************************************************/
532 static int IsLocalLabel (const Line* L)
533 /* Return true if the line is a local label line */
535 return (L->Line [0] == 'L' && IsXDigit (L->Line [1]));
540 static int IsExtLabel (const Line* L)
541 /* Return true if the line is an external label line */
543 return (L->Line [0] == '_');
548 static int IsLabel (const Line* L)
549 /* Return true if the line is a label line */
551 return (L->Line [0] == 'L' && IsXDigit (L->Line [1])) ||
552 (L->Line [0] == '_');;
557 static int IsHintLine (const Line* L)
558 /* Return true if the line contains an optimizer hint */
560 return L->Line [0] == '+';
565 static int IsSegHint (const Line* L)
566 /* Return true if the given line contains a segment hint */
568 return (L->Line [0] == '+' && strncmp (L->Line + 1, "seg:", 4) == 0);
573 static int IsHint (const Line* L, const char* Hint)
574 /* Check if the line contains a given hint */
576 return (L->Line [0] == '+' && strcmp (L->Line + 1, Hint) == 0);
581 static int IsCondJump (const Line* L)
582 /* Return true if the line contains a conditional jump */
584 return (L->Line [0] == '\t' &&
585 (strncmp (L->Line + 1, "beq\t", 4) == 0 ||
586 strncmp (L->Line + 1, "bne\t", 4) == 0 ||
587 strncmp (L->Line + 1, "jeq\t", 4) == 0 ||
588 strncmp (L->Line + 1, "jne\t", 4) == 0));
593 static int IsXAddrMode (const Line* L)
594 /* Return true if the given line does use the X register */
596 unsigned Len = strlen (L->Line);
597 return (strcmp (L->Line + Len - 3, ",x)") == 0 ||
598 strcmp (L->Line + Len - 2, ",x") == 0);
603 static int NoXAddrMode (const Line* L)
604 /* Return true if the given line does use the X register */
606 return !IsXAddrMode (L);
611 static int IsYAddrMode (const Line* L)
612 /* Return true if the given line does use the Y register */
614 unsigned Len = strlen (L->Line);
615 return (strcmp (L->Line + Len - 2, ",y") == 0);
620 static int Is16BitStore (const Line* L1, const Line* L2)
621 /* Check if L1 and L2 are a store of ax into a 16 bit location */
623 unsigned Len1 = strlen (L1->Line);
624 return (strncmp (L1->Line, "\tsta\t", 5) == 0 &&
625 strncmp (L2->Line, "\tstx\t", 5) == 0 &&
626 strncmp (L1->Line+5, L2->Line+5, Len1-5) == 0 &&
627 strcmp (L2->Line+Len1, "+1") == 0);
632 static Line* FindHint (Line* L, const char* Hint)
633 /* Search for a line with the given hint */
636 if (IsHint (L, Hint)) {
646 static unsigned GetHexNum (const char* S)
647 /* Get a hex number from a string */
651 while (IsXDigit (S [I])) {
652 int C = (unsigned char) (S [I++]);
658 Val = (Val << 4) + C;
665 static unsigned GetLabelNum (const char* L)
666 /* Return the label number of a label line */
669 return GetHexNum (L+1);
674 static Line* GetTargetLine (const char* L)
675 /* Get the line with the target label of a jump. L must be a pointer to the
676 * string containing the label number.
681 /* Get the label number of the target */
682 unsigned Label = GetLabelNum (L);
683 CHECK (Label < LabelCount);
685 /* Get the line with this label */
686 Target = Labels [Label];
687 CHECK (Target != 0 && (Target->Flags & OF_CODE) != 0);
695 static unsigned GetJumpDistance (Line* L, Line* Target)
696 /* Get the distance between both lines */
698 unsigned Distance = 0;
701 if (Target->Index > L->Index) {
702 /* This is a forward jump. */
704 L = NextCodeLine (L);
706 } while (L != Target);
708 /* This is a backward jump */
710 L = PrevCodeLine (L);
712 } while (L != Target);
716 /* Return the calculated distance */
722 static int LineMatch (const Line* L, const char* Start)
723 /* Check if the start of the line matches Start */
725 return strncmp (L->Line, Start, strlen (Start)) == 0;
730 static int LineFullMatch (const Line* L, const char* Start)
731 /* Check if the matches Start */
733 return strcmp (L->Line, Start) == 0;
738 static int LineMatchX (const Line* L, const char** Start)
739 /* Check the start of the line against a list of patterns. Return the
740 * number of the pattern that matched, or -1 in case of no match.
745 if (LineMatch (L, *Start)) {
758 static int LineFullMatchX (const Line* L, const char** Start)
759 /* Check the the line against a list of patterns. Return the
760 * number of the pattern that matched, or -1 in case of no match.
765 if (LineFullMatch (L, *Start)) {
778 static int IsLoadAX (Line* L1, Line* L2)
779 /* Check if the both lines load a static variable into ax. That is, both lines
785 return LineMatch (L1, "\tlda\t") &&
786 LineMatch (L2, "\tldx\t") &&
787 strncmp (L1->Line+5, L2->Line+5, strlen (L1->Line+5)) == 0 &&
788 strcmp (L2->Line+strlen(L1->Line), "+1") == 0;
793 /*****************************************************************************/
794 /* Initial optimizer setup */
795 /*****************************************************************************/
799 static void FindCodeStart (void)
800 /* Find and remember the first line of actual code */
802 Line* L = FindHint (FirstLine, "end_of_preamble");
803 FirstCode = L? L->Next : 0;
808 static unsigned EstimateDataSize (Line* L, unsigned Chunk)
809 /* Estimate the size of a .byte, .word or .dword command */
811 unsigned Size = Chunk;
813 while ((S = strchr (S, ',')) != 0) {
822 static unsigned EstimateSize (Line* L)
823 /* Estimate the size of an instruction */
825 static const char* OneByteCmds [] = {
841 if (L->Line [0] != '\t') {
844 if (LineMatch (L, "\tldax\t")) {
845 /* Immidiate load of both, A and X */
848 if (LineMatch (L, "\tld")) {
849 OpStart = L->Line [5];
850 return (OpStart == '#' || OpStart == '(')? 2 : 3;
852 if (LineMatch (L, "\tst")) {
853 OpStart = L->Line [5];
854 return (OpStart == '(')? 2 : 3;
856 if (LineMatch (L, "\t.byte\t")) {
857 return EstimateDataSize (L, 1);
859 if (LineMatch (L, "\t.word\t")) {
860 return EstimateDataSize (L, 2);
862 if (LineMatch (L, "\t.dword\t")) {
863 return EstimateDataSize (L, 4);
865 if (LineMatchX (L, ShortBranches) >= 0) {
868 if (LineMatchX (L, LongBranches) >= 0) {
871 if (LineMatchX (L, OneByteCmds) >= 0) {
879 static void MarkCodeLines (void)
880 /* Mark all lines that are inside a code segment */
886 InCode = IsHint (L, "seg:code");
887 } else if (InCode && L->Line[0] != '\0') {
889 L->Size = EstimateSize (L);
897 static void CreateLabelList (void)
898 /* Create a list with pointers to local labels */
904 /* Get the next label number. This is also the current label count.
905 * Make some room for more labels when optimizing code.
907 LabelCount = GetLabel () + 100;
909 /* Allocate memory for the array and clear it */
910 Labels = xmalloc (LabelCount * sizeof (Line*));
911 for (I = 0; I < LabelCount; ++I) {
915 /* Walk through the code and insert all label lines */
918 if (IsLocalLabel (L)) {
919 unsigned LabelNum = GetLabelNum (L->Line);
920 CHECK (LabelNum < LabelCount);
921 Labels [LabelNum] = L;
929 static unsigned AllocLabel (void)
930 /* Get a new label. The current code does not realloc the label list, so there
931 * must be room enough in the current list.
936 /* Search for a free slot, start at 1, since 0 is "no label" */
937 for (I = 1; I < LabelCount; ++I) {
938 if (Labels[I] == 0) {
939 /* Found a free slot */
944 /* No label space available */
945 Internal ("Out of label space in the optimizer");
953 /*****************************************************************************/
954 /* Helper functions */
955 /*****************************************************************************/
959 static int GetNextCodeLines (Line* L, Line** Lines, unsigned Count)
960 /* Get a number of code lines ignoring hints and other stuff. The function
961 * returns 1 if we got the lines and 0 if we are at the end of the code
962 * segment or if we hit a label.
967 /* Get the next valid line */
969 L = NextCodeLine (L);
970 } while (L && IsHintLine (L));
972 /* Did we get one? */
973 if (L == 0 || IsLabel (L)) {
978 /* Remember the line */
988 static int FindCond (const char* Suffix)
989 /* Map a condition suffix to a code. Return the code or -1 on failure */
994 for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) {
995 if (strncmp (Suffix, CmpSuffixTab [I], strlen (CmpSuffixTab[I])) == 0) {
1007 static int CheckAndGetIntCmp (const Line* JSR, const Line* JMP)
1008 /* Helper function to check for a compare subroutine call followed by a
1009 * conditional branch. Will return the condition found, or -1 if no
1010 * or invalid condition.
1017 /* Extract the condition from the function name. */
1018 if ((Cond [0] = JSR->Line [8]) == 'u') {
1019 Cond [1] = JSR->Line [9];
1020 Cond [2] = JSR->Line [10];
1022 Tail = JSR->Line + 11;
1024 Cond [1] = JSR->Line [9];
1026 Tail = JSR->Line + 10;
1029 /* Check if this is indeed an integer function */
1030 if (strcmp (Tail, "ax") != 0) {
1035 /* Get the condition code */
1036 C = FindCond (Cond);
1042 /* Invert the code if we jump on condition not met. */
1043 if (JMP->Line [2] == 'e' && JMP->Line [3] == 'q') {
1044 /* Jumps if condition false, invert condition */
1045 C = CmpInvertTab [C];
1048 /* Return the condition code */
1054 static int TosCmpFunc (Line* L)
1055 /* Check if this is a call to one of the TOS compare functions (tosgtax).
1056 * Return the condition code or -1 if not found.
1059 if (LineMatch (L, "\tjsr\ttos") &&
1060 strcmp (L->Line+strlen(L->Line)-2, "ax") == 0) {
1062 /* Ok, found. Get the condition. */
1063 return FindCond (L->Line+8);
1074 static int IsUnsignedCmp (int Code)
1075 /* Check if this is an unsigned compare */
1078 return CmpSignedTab [Code] == 0;
1083 static void InvertZJump (Line* L)
1084 /* Invert a jeq/jne jump */
1086 if (L->Line [2] == 'n' && L->Line [3] == 'e') {
1087 /* This was a bne/jne */
1091 /* This was (hopefully) a beq/jeq */
1099 static int FindCmd (Line* L)
1103 /* Search for the known patterns */
1104 for (I = 0; I < COUNT(CmdDesc); ++I) {
1105 if (CmdDesc[I].FullMatch) {
1106 if (LineFullMatch (L, CmdDesc[I].Insn)) {
1111 if (LineMatch (L, CmdDesc[I].Insn)) {
1123 static unsigned RVUInt2 (Line* L,
1124 LineColl* LC, /* To remember visited lines */
1125 unsigned Used, /* Definitely used registers */
1126 unsigned Unused) /* Definitely unused registers */
1127 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
1131 /* Check the following instructions. We classifiy them into primary
1132 * loads (register value not used), neutral (check next instruction),
1133 * and unknown (assume register was used).
1139 /* Get the next line and follow jumps */
1142 /* Handle jumps to local labels (continue there) */
1143 if (LineMatch (L, "\tjmp\tL") || LineMatch (L, "\tbra\tL")) {
1144 /* Get the target of the jump */
1145 L = GetTargetLine (L->Line+5);
1148 /* Get the next line, skip local labels */
1150 L = NextCodeSegLine (L);
1151 } while (L && (IsLocalLabel (L) || L->Line[0] == '\0'));
1153 /* Bail out if we're done */
1154 if (L == 0 || IsExtLabel (L)) {
1155 /* End of function reached */
1159 /* Check if we had this line already. If so, bail out, if not,
1160 * add it to the list of known lines.
1162 if (LCHasLine (LC, L) || !LCAddLine (LC, L)) {
1166 } while (LineMatch (L, "\tjmp\tL") || LineMatch (L, "\tbra\tL"));
1168 /* Special handling of code hints */
1169 if (IsHintLine (L)) {
1171 if (IsHint (L, "a:-") && (Used & REG_A) == 0) {
1173 } else if (IsHint (L, "x:-") && (Used & REG_X) == 0) {
1175 } else if (IsHint (L, "y:-") && (Used & REG_Y) == 0) {
1179 /* Special handling for branches */
1180 } else if (LineMatchX (L, ShortBranches) >= 0 ||
1181 LineMatchX (L, LongBranches) >= 0) {
1182 const char* Target = L->Line+5;
1183 if (Target[0] == 'L') {
1184 /* Jump to local label. Check the register usage starting at
1185 * the branch target and at the code following the branch.
1186 * All registers that are unused in both execution flows are
1187 * returned as unused.
1190 U2 = RVUInt1 (GetTargetLine (Target), LC, Used, Unused);
1191 U1 = RVUInt1 (L, LC, Used, Unused);
1192 return U1 | U2; /* Used in any of the branches */
1196 /* Search for the instruction in this line */
1199 /* If we don't find it, assume all other registers are used */
1204 /* Evaluate the use flags, check for addressing modes */
1206 if (IsXAddrMode (L)) {
1208 } else if (IsYAddrMode (L)) {
1212 /* Remove registers that were already new loaded */
1215 /* Remember the remaining registers */
1219 /* Evaluate the load flags */
1220 R = CmdDesc[I].Load;
1222 /* Remove registers that were already used */
1225 /* Remember the remaining registers */
1231 /* If we know about all registers, bail out */
1232 if ((Used | Unused) == REG_ALL) {
1238 /* Return to the caller the complement of all unused registers */
1239 return ~Unused & REG_ALL;
1244 static unsigned RVUInt1 (Line* L,
1245 LineColl* LC, /* To remember visited lines */
1246 unsigned Used, /* Definitely used registers */
1247 unsigned Unused) /* Definitely unused registers */
1248 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
1250 /* Remember the current count of the line collection */
1251 unsigned Count = LC->Count;
1253 /* Call the worker routine */
1254 unsigned R = RVUInt2 (L, LC, Used, Unused);
1256 /* Restore the old count */
1259 /* Return the result */
1265 static unsigned RegValUsed (Line* Start)
1266 /* Check the next instructions after the one in L for register usage. If
1267 * a register is used as an index, or in a store or other instruction, it
1268 * is assumed to be used. If a register is loaded with a value, before it
1269 * was used by one of the actions described above, it is assumed unused.
1270 * If the end of the lookahead is reached, all registers that are uncertain
1271 * are marked as used.
1272 * The result of the search is returned.
1277 /* Create a new line collection and enter the start line */
1278 LineColl* LC = NewLineColl (256);
1279 LCAddLine (LC, Start);
1281 /* Call the recursive subfunction */
1282 R = RVUInt1 (Start, LC, REG_NONE, REG_NONE);
1284 /* Delete the line collection */
1287 /* Return the registers used */
1293 static int RegAUsed (Line* Start)
1294 /* Check if the value in A is used. */
1296 return (RegValUsed (Start) & REG_A) != 0;
1301 static int RegXUsed (Line* Start)
1302 /* Check if the value in X is used. */
1304 return (RegValUsed (Start) & REG_X) != 0;
1309 static int RegYUsed (Line* Start)
1310 /* Check if the value in Y is used. */
1312 return (RegValUsed (Start) & REG_Y) != 0;
1317 /*****************************************************************************/
1318 /* Real optimizer routines */
1319 /*****************************************************************************/
1323 static void OptCompares1 (void)
1324 /* Try to optimize the integer compare subroutines. */
1326 Line* L2[10]; /* Line lookahead */
1327 int Cond; /* Condition to evaluate */
1328 unsigned Label; /* Local label number */
1329 unsigned Offs; /* Stack offset */
1330 Line* DelStart; /* First line to delete */
1332 Line* L = FirstCode;
1335 /* Search for compares of local byte sized variables. This looks
1347 * Replace it by a direct compare:
1355 if (LineMatch (L, "\tldy\t#$") &&
1356 GetNextCodeLines (L, L2, 7) &&
1357 LineFullMatch (L2[0], "\tldx\t#$00") &&
1358 LineFullMatch (L2[1], "\tlda\t(sp),y") &&
1359 LineFullMatch (L2[2], "\tjsr\tpushax") &&
1360 LineMatch (L2[3], "\tldy\t#$") &&
1361 LineFullMatch (L2[4], "\tldx\t#$00") &&
1362 LineFullMatch (L2[5], "\tlda\t(sp),y") &&
1363 (Cond = TosCmpFunc (L2[6])) >= 0) {
1365 /* Get the stack offset and correct it, since we will remove
1368 Offs = GetHexNum (L2[3]->Line+7) - 2;
1371 L = NewLineAfter (L, "\tlda\t(sp),y");
1372 L = NewLineAfter (L, "\tldy\t#$%02X", Offs);
1373 L = NewLineAfter (L, "\tcmp\t(sp),y");
1374 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1376 /* Remove the old cruft */
1377 FreeLines (L2[0], L2[6]);
1380 /* Compares of byte sized global variables */
1381 else if (LineFullMatch (L, "\tldx\t#$00") &&
1382 GetNextCodeLines (L, L2, 5) &&
1383 LineMatch (L2[0], "\tlda\t") &&
1384 LineFullMatch (L2[1], "\tjsr\tpushax") &&
1385 LineFullMatch (L2[2], "\tldx\t#$00") &&
1386 LineMatch (L2[3], "\tlda\t") &&
1387 (Cond = TosCmpFunc (L2[4])) >= 0) {
1390 if (IsXAddrMode (L2[0])) {
1391 /* The load is X indirect, so we may not remove the load
1392 * of the X register.
1397 L = ReplaceLine (L, L2[0]->Line);
1400 L = NewLineAfter (L, "\tcmp\t%s", L2[3]->Line+5);
1401 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1403 /* Remove the old cruft */
1404 FreeLines (DelStart, L2[4]);
1408 /* Byte sized local to global */
1409 else if (LineMatch (L, "\tldy\t#$") &&
1410 GetNextCodeLines (L, L2, 6) &&
1411 LineFullMatch (L2[0], "\tldx\t#$00") &&
1412 LineFullMatch (L2[1], "\tlda\t(sp),y") &&
1413 LineFullMatch (L2[2], "\tjsr\tpushax") &&
1414 LineFullMatch (L2[3], "\tldx\t#$00") &&
1415 LineMatch (L2[4], "\tlda\t") &&
1416 (Cond = TosCmpFunc (L2[5])) >= 0) {
1419 L = NewLineAfter (L, L2[1]->Line);
1420 L = NewLineAfter (L, "\tcmp\t%s", L2[4]->Line+5);
1421 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1423 /* Remove the old cruft */
1424 FreeLines (L2[0], L2[5]);
1428 /* Byte sized global to local */
1429 else if (LineFullMatch (L, "\tldx\t#$00") &&
1430 GetNextCodeLines (L, L2, 6) &&
1431 LineMatch (L2[0], "\tlda\t") &&
1432 LineFullMatch (L2[1], "\tjsr\tpushax") &&
1433 LineMatch (L2[2], "\tldy\t#$") &&
1434 LineFullMatch (L2[3], "\tldx\t#$00") &&
1435 LineFullMatch (L2[4], "\tlda\t(sp),y") &&
1436 (Cond = TosCmpFunc (L2[5])) >= 0) {
1438 /* Get the stack offset and correct it, since we will remove
1441 Offs = GetHexNum (L2[2]->Line+7) - 2;
1444 if (IsXAddrMode (L2[0])) {
1445 /* The load is X indirect, so we may not remove the load
1446 * of the X register.
1451 L = ReplaceLine (L, L2[0]->Line);
1454 L = NewLineAfter (L, "\tldy\t#$%02X", Offs);
1455 L = NewLineAfter (L, "\tcmp\t(sp),y");
1456 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1458 /* Remove the old cruft */
1459 FreeLines (DelStart, L2[5]);
1463 /* Search for unsigned compares against global variables. This looks
1471 * Replace that by a direct compare:
1479 else if (LineFullMatch (L, "\tjsr\tpushax") &&
1480 GetNextCodeLines (L, L2, 3) &&
1481 IsLoadAX (L2[0], L2[1]) &&
1482 (Cond = TosCmpFunc (L2[2])) >= 0 &&
1483 IsUnsignedCmp (Cond)) {
1485 /* Get a free label number */
1486 Label = AllocLabel ();
1488 /* Replace the code */
1489 L = ReplaceLine (L, "\tcpx\t%s", L2[1]->Line+5);
1490 L = NewLineAfter (L, "\tbne\tL%04X", Label);
1491 L = NewLineAfter (L, "\tcmp\t%s", L2[0]->Line+5);
1492 L = NewLabelAfter(L, Label);
1493 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1495 /* Remove the old code */
1496 FreeLines (L2[0], L2[2]);
1500 L = NextCodeLine (L);
1506 static void OptDeadJumps (void)
1507 /* Remove jumps to the following instruction */
1509 static const char* Jumps [] = {
1518 Line* L = FirstCode;
1521 /* Get a pointer to the next instruction line */
1522 Line* NextLine = NextInstruction (L);
1524 /* Is this line a jump? */
1525 int I = LineMatchX (L, Jumps);
1527 /* Yes. Get the target label, skip labels */
1528 Line* Target = NextInstruction (GetTargetLine (L->Line+5));
1530 /* If the target label is the next line, remove the jump */
1531 if (Target == NextLine) {
1536 /* Go to the next line */
1543 static void OptLoads (void)
1544 /* Remove unnecessary loads of values */
1548 Line* L = FirstCode;
1569 * This change will cost 3 cycles (one additional jump inside the
1570 * subroutine), but it saves a lot of code (6 bytes per occurrence),
1571 * so we will accept the overhead. It may even be possible to rewrite
1572 * the library routine to get rid of the additional overhead.
1574 if (LineMatch (L, "\tldy\t#$") &&
1575 GetNextCodeLines (L, L2, 5) &&
1576 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
1577 LineFullMatch (L2 [1], "\ttax") &&
1578 LineFullMatch (L2 [2], "\tdey") &&
1579 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
1580 LineFullMatch (L2 [4], "\tjsr\tpushax")) {
1582 /* Found - replace it */
1583 if (LineFullMatch (L, "\tldy\t#$01")) {
1584 /* Word at offset zero */
1586 L = ReplaceLine (L2 [4], "\tjsr\tpushw0sp");
1588 ReplaceLine (L2 [4], "\tjsr\tpushwysp");
1591 /* Delete the remaining lines */
1592 FreeLines (L2 [0], L2 [3]);
1614 * This change will cost 2 cycles, but it saves a lot of code (6 bytes
1615 * per occurrence), so we will accept the overhead. It may even be
1616 * possible to rewrite the library routine to get rid of the additional
1619 } else if (LineMatch (L, "\tldy\t#$") &&
1620 GetNextCodeLines (L, L2, 6) &&
1621 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
1622 LineFullMatch (L2 [1], "\ttax") &&
1623 LineFullMatch (L2 [2], "\tdey") &&
1624 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
1625 LineMatch (L2 [4], "\tldy\t#$") &&
1626 LineFullMatch (L2 [5], "\tjsr\tldauidx")) {
1628 /* Found - replace it */
1629 L2 [4]->Line [3] = 'x'; /* Change to ldx */
1630 if (LineFullMatch (L, "\tldy\t#$01")) {
1631 /* Word at offset zero */
1633 L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp");
1635 ReplaceLine (L2 [5], "\tjsr\tldauiysp");
1638 /* Delete the remaining lines */
1639 FreeLines (L2 [0], L2 [3]);
1650 } else if (LineFullMatch (L, "\tlda\t(sp),y") &&
1651 GetNextCodeLines (L, L2, 1) &&
1652 LineFullMatch (L2 [0], "\tjsr\tpusha")) {
1654 /* Found, replace it */
1655 L = ReplaceLine (L, "\tjsr\tpushaysp");
1665 * and replace it by:
1672 * provided that that the X register is not used later. While this is
1673 * no direct optimization, it helps with other optimizations.
1675 } else if (LineMatch (L, "\tldx\t") &&
1676 GetNextCodeLines (L, L2, 3) &&
1677 LineMatch (L2 [0], "\tlda\t") &&
1678 Is16BitStore (L2[1], L2[2]) &&
1679 !RegXUsed (L2[2])) {
1681 /* Found - replace it */
1683 NewLineAfter (L, "\tsta\t%s", L2[2]->Line+5);
1694 * and replace it by:
1703 * provided that that the X register is not used later. This code
1704 * sequence is two bytes longer, but a lot faster and it does not
1705 * use the X register, so other loads may get removed later.
1707 } else if (LineMatch (L, "\tldx\t") &&
1708 GetNextCodeLines (L, L2, 3) &&
1709 LineMatch (L2 [0], "\tlda\t") &&
1710 LineMatch (L2 [1], "\tldy\t#$") &&
1711 LineFullMatch (L2 [2], "\tjsr\tstaxysp") &&
1712 !RegXUsed (L2[2])) {
1714 /* Found - replace it */
1716 L = NewLineAfter (L, "\tldy\t#$%02X", GetHexNum (L2[1]->Line+7)+1);
1717 L = NewLineAfter (L, "\tsta\t(sp),y");
1718 L = NewLineAfter (L, "\tdey");
1719 L = NewLineAfter (L2[0], "\tsta\t(sp),y");
1721 /* Remove the remaining lines */
1722 FreeLines (L2[1], L2[2]);
1730 * and replace it by:
1739 * provided that that the X register is not used later. This code
1740 * sequence is four bytes longer, but a lot faster and it does not
1741 * use the X register, so other loads may get removed later.
1743 } else if (LineMatch (L, "\tldx\t") &&
1744 GetNextCodeLines (L, L2, 2) &&
1745 LineMatch (L2 [0], "\tlda\t") &&
1746 LineFullMatch (L2 [1], "\tjsr\tstax0sp") &&
1747 !RegXUsed (L2[1])) {
1749 /* Found - replace it */
1751 L = NewLineAfter (L, "\tldy\t#$01");
1752 L = NewLineAfter (L, "\tsta\t(sp),y");
1753 L = NewLineAfter (L, "\tdey");
1754 L = NewLineAfter (L2[0], "\tsta\t(sp),y");
1756 /* Remove the remaining line */
1760 /* All other patterns start with this one: */
1761 if (!LineFullMatch (L, "\tldx\t#$00")) {
1771 * and replace it by:
1776 if (GetNextCodeLines (L, L2, 1) &&
1777 LineFullMatch (L2 [0], "\tjsr\tpushax")) {
1779 /* Replace the subroutine call */
1780 L = ReplaceLine (L, "\tjsr\tpusha0");
1782 /* Remove the unnecessary line */
1792 * and replace it by:
1798 else if (GetNextCodeLines (L, L2, 2) &&
1799 LineMatch (L2 [0], "\tlda\t") &&
1800 LineFullMatch (L2 [1], "\tjsr\tpushax")) {
1802 /* Be sure, X is not used in the load */
1803 if (NoXAddrMode (L2 [0])) {
1805 /* Replace the subroutine call */
1806 L2 [1] = ReplaceLine (L2 [1], "\tjsr\tpusha0");
1808 /* Remove the unnecessary load */
1811 /* L must be valid */
1823 * and replace it by:
1828 else if (GetNextCodeLines (L, L2, 2) &&
1829 LineMatch (L2 [0], "\tlda\t") &&
1830 LineMatch (L2 [1], "\tcmp\t#$")) {
1832 /* Be sure, X is not used in the load */
1833 if (NoXAddrMode (L2 [0])) {
1835 /* Remove the unnecessary load */
1838 /* L must be valid */
1849 * and replace it by:
1854 else if (GetNextCodeLines (L, L2, 2) &&
1855 LineMatch (L2 [0], "\tlda\t") &&
1856 LineFullMatch (L2 [1], "\tjsr\tbnega")) {
1858 /* Be sure, X is not used in the load */
1859 if (NoXAddrMode (L2 [0])) {
1861 /* Remove the unnecessary load */
1864 /* L must be valid */
1870 /* Go to the next line */
1871 L = NextCodeLine (L);
1877 static void OptRegLoads (void)
1878 /* Remove unnecessary loads of registers */
1884 /* Repeat this until there is nothing more to delete */
1892 /* Search for a load of X and check if the value is used later */
1893 if (LineMatch (L, "\tldx\t") &&
1895 !IsCondJump (NextInstruction (L))) {
1897 /* Remember to delete this line */
1901 /* Search for a load of A and check if the value is used later */
1902 else if (LineMatch (L, "\tlda\t") &&
1904 !IsCondJump (NextInstruction (L))) {
1906 /* Remember to delete this line */
1910 /* Search for a load of Y and check if the value is used later */
1911 else if (LineMatch (L, "\tldy\t") &&
1913 !IsCondJump (NextInstruction (L))) {
1915 /* Remember to delete this line */
1919 /* Go to the next line, delete the current if requested */
1921 L = NextCodeLine (L);
1927 } while (Deletions > 0);
1932 static int OptPtrOps1 (Line** Start)
1933 /* Optimize several pointer and array constructs - subfunction 1 */
1938 unsigned LinesToRemove;
1943 /* Use a local variable for the working line */
1946 /* Search for (23B/XXT)
1958 * and replace it by something like (24B/26T)
1972 if (!LineMatch (L, "\tlda\t") ||
1973 !GetNextCodeLines (L, L2, 4) ||
1974 !IsLoadAX (L, L2 [0]) ||
1975 !LineFullMatch (L2[1], "\tsta\tregsave") ||
1976 !LineFullMatch (L2[2], "\tstx\tregsave+1")) {
1983 if (LineMatch (L2[3], "\tjsr\tincax")) {
1984 /* Get next code lines */
1985 if (GetNextCodeLines (L2[3], &L2[4], 4) == 0) {
1986 /* Cannot get lines */
1989 Inc = GetHexNum (L2[3]->Line+10);
1993 /* Get next code lines */
1994 if (GetNextCodeLines (L2[3], &L2[4], 7) == 0) {
1995 /* Cannot get lines */
1998 if (LineFullMatch (L2[3], "\tclc") &&
1999 LineMatch (L2[4], "\tadc\t#$") &&
2000 LineFullMatch (L2[5], "\tbcc\t*+3") &&
2001 LineFullMatch (L2[6], "\tinx")) {
2002 /* Inlined increment */
2003 Inc = GetHexNum (L2[4]->Line+7);
2012 /* Check for the remainder */
2013 if (!LineMatch (L3[0], "\tsta\t") ||
2014 strcmp (L3[0]->Line+5, L->Line+5) != 0 ||
2015 !LineMatch (L3[1], "\tstx\t") ||
2016 strcmp (L3[1]->Line+5, L2[0]->Line+5) != 0 ||
2017 !LineFullMatch (L3[2], "\tlda\tregsave") ||
2018 !LineFullMatch (L3[3], "\tldx\tregsave+1")) {
2024 /* Check if AX is actually used following the code above. If not,
2025 * we don't need to load A/X from regsave. Since X will never be
2026 * used without A, check just for A.
2028 NeedLoad = RegAUsed (L3[3]);
2030 /* Special code for register variables */
2032 if (LineMatch (L, "\tlda\tregbank+") &&
2033 GetNextCodeLines (L3[3], &L3[4], 1) &&
2036 /* Remember the offset into the register bank */
2038 strcpy (Reg, L->Line+5);
2040 /* Check for several special sequences */
2041 if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
2042 /* Load char indirect */
2043 L = ReplaceLine (L, "\tldx\t#$00");
2044 L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
2045 L = NewLineAfter (L, "\tinc\t%s", Reg);
2046 L = NewLineAfter (L, "\tbne\t*+4");
2047 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
2050 } else if (LineFullMatch (L3[4], "\tsta\tptr1") &&
2051 GetNextCodeLines (L3[4], &L3[5], 3) &&
2052 LineFullMatch (L3[5], "\tstx\tptr1+1") &&
2053 LineFullMatch (L3[6], "\tldx\t#$00") &&
2054 LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
2056 /* Load char indirect, inlined */
2057 L = ReplaceLine (L, "\tldx\t#$00");
2058 L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
2059 L = NewLineAfter (L, "\tinc\t%s", Reg);
2060 L = NewLineAfter (L, "\tbne\t*+4");
2061 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
2065 } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
2066 if (GetNextCodeLines (L3[4], &L3[5], 2) &&
2067 LineMatch (L3[5], "\tlda\t") &&
2068 LineFullMatch (L3[6], "\tjsr\tstaspp")) {
2070 /* Store to pointer */
2071 L = ReplaceLine (L, L3[5]->Line);
2072 L = NewLineAfter (L, "\tldy\t#$00");
2073 L = NewLineAfter (L, "\tsta\t(%s),y", Reg);
2074 L = NewLineAfter (L, "\tinc\t%s", Reg);
2075 L = NewLineAfter (L, "\tbne\t*+4");
2076 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
2081 } else if (GetNextCodeLines (L3[4], &L3[5], 3) &&
2082 LineMatch (L3[5], "\tldy\t#$") &&
2083 LineFullMatch (L3[6], "\tlda\t(sp),y") &&
2084 LineFullMatch (L3[7], "\tjsr\tstaspp")) {
2086 /* Beware: We have to correct the stack offset, since we will
2087 * remove the pushax instruction!
2089 Offs = GetHexNum (L3[5]->Line+7) - 2;
2091 /* Store to pointer */
2092 L = ReplaceLine (L, "\tldy\t#$%02X", Offs);
2093 L = NewLineAfter (L, "\tldx\t#$00");
2094 L = NewLineAfter (L, "\tlda\t(sp),y");
2095 L = NewLineAfter (L, "\tsta\t(%s,x)", Reg);
2096 L = NewLineAfter (L, "\tinc\t%s", Reg);
2097 L = NewLineAfter (L, "\tbne\t*+4");
2098 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
2108 /* No register variable - insert the first part of the code */
2110 L = NewLineAfter (L, "\tsta\tptr1");
2112 L = NewLineAfter (L, "\tclc");
2113 L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
2114 L = NewLineAfter (L, "\tsta\t%s", L3[0]->Line+5);
2115 L = NewLineAfter (L, "\tlda\t%s", L3[1]->Line+5);
2117 L = NewLineAfter (L, "\tsta\tptr1+1");
2119 L = NewLineAfter (L, "\tadc\t#$00");
2120 L = NewLineAfter (L, "\tsta\t%s", L3[1]->Line+5);
2122 /* Check if we must really load the old value into a/x or if the
2123 * code may be replaced by something else.
2125 if (GetNextCodeLines (L3[3], &L3[4], 1)) {
2126 if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
2127 /* Load char indirect */
2128 L = NewLineAfter (L, "\tldx\t#$00");
2129 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2132 } else if (LineFullMatch (L3[4], "\tsta\tptr1") &&
2133 GetNextCodeLines (L3[4], &L3[5], 3) &&
2134 LineFullMatch (L3[5], "\tstx\tptr1+1") &&
2135 LineFullMatch (L3[6], "\tldx\t#$00") &&
2136 LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
2138 /* Load char indirect, inlined */
2139 L = NewLineAfter (L, "\tldx\t#$00");
2140 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2144 } else if (LineFullMatch (L3[4], "\tjsr\tldaxi")) {
2145 /* Load word indirect */
2146 L = NewLineAfter (L, "\tldy\t#$01");
2147 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2148 L = NewLineAfter (L, "\ttax");
2149 L = NewLineAfter (L, "\tdey");
2150 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2154 } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
2155 if (GetNextCodeLines (L3[4], &L3[5], 2) &&
2156 LineMatch (L3[5], "\tlda\t") &&
2157 LineFullMatch (L3[6], "\tjsr\tstaspp")) {
2159 /* Store to pointer */
2160 L = NewLineAfter (L, L3[5]->Line);
2161 L = NewLineAfter (L, "\tldy\t#$00");
2162 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2166 } else if (GetNextCodeLines (L3[4], &L3[5], 3) &&
2167 LineMatch (L3[5], "\tldy\t#$") &&
2168 LineFullMatch (L3[6], "\tlda\t(sp),y") &&
2169 LineFullMatch (L3[7], "\tjsr\tstaspp")) {
2171 /* Beware: We have to correct the stack offset, since we will
2172 * remove the pushax instruction!
2174 sprintf (L3[5]->Line+7, "%02X", GetHexNum (L3[5]->Line+7)-2);
2176 /* Store to pointer */
2177 L = NewLineAfter (L, L3[5]->Line);
2178 L = NewLineAfter (L, L3[6]->Line);
2179 L = NewLineAfter (L, "\tldy\t#$00");
2180 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2189 /* If we need to load a/x, add the code */
2191 L = NewLineAfter (L, "\tlda\tptr1");
2192 L = NewLineAfter (L, "\tldx\tptr1+1");
2196 /* Remove the code that is no longer needed */
2197 FreeLines (L2[0], L2[LinesToRemove-1]);
2199 /* Return the new line and success */
2200 *Start = NextCodeLine (L);
2206 static int OptPtrOps2 (Line** Start)
2207 /* Optimize several pointer and array constructs - subfunction 2 */
2212 unsigned LinesToRemove;
2217 /* Use a local variable for the working line */
2220 /* Same as subfunction 1 but for local variables. */
2221 if (LineMatch (L, "\tldy\t#$") == 0) {
2225 /* Get the stack offset. The offset points to the high byte, correct that. */
2226 Offs = GetHexNum (L->Line+7) - 1;
2228 /* Check for the actual sequences */
2229 if (GetNextCodeLines (L, L2, 7) &&
2230 LineFullMatch (L2[0], "\tjsr\tldaxysp") &&
2231 LineFullMatch (L2[1], "\tsta\tregsave") &&
2232 LineFullMatch (L2[2], "\tstx\tregsave+1") &&
2233 LineMatch (L2[3], "\tjsr\tincax")) {
2235 /* Non inlined version */
2236 Inc = GetHexNum (L2[3]->Line+10);
2238 /* Check for stack offset zero */
2239 if (LineFullMatch (L2[4], "\tjsr\tstax0sp") &&
2240 LineFullMatch (L2[5], "\tlda\tregsave") &&
2241 LineFullMatch (L2[6], "\tldx\tregsave+1")) {
2245 } else if (GetNextCodeLines (L2[6], &L2[7], 1) &&
2246 LineMatch (L2[4], "\tldy\t#$") &&
2247 GetHexNum (L2[4]->Line+7) == Offs &&
2248 LineFullMatch (L2[5], "\tjsr\tstaxysp") &&
2249 LineFullMatch (L2[6], "\tlda\tregsave") &&
2250 LineFullMatch (L2[7], "\tldx\tregsave+1")) {
2259 } else if (GetNextCodeLines (L, L2, 13) &&
2260 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
2261 LineFullMatch (L2[1], "\ttax") &&
2262 LineFullMatch (L2[2], "\tdey") &&
2263 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
2264 LineFullMatch (L2[4], "\tsta\tregsave") &&
2265 LineFullMatch (L2[5], "\tstx\tregsave+1") &&
2266 LineFullMatch (L2[6], "\tclc") &&
2267 LineMatch (L2[7], "\tadc\t#$") &&
2268 LineFullMatch (L2[8], "\tbcc\t*+3") &&
2269 LineFullMatch (L2[9], "\tinx")) {
2271 /* Inlined version */
2272 Inc = GetHexNum (L2[7]->Line+7);
2274 /* Check for stack offset zero */
2275 if (LineFullMatch (L2[10], "\tjsr\tstax0sp") &&
2276 LineFullMatch (L2[11], "\tlda\tregsave") &&
2277 LineFullMatch (L2[12], "\tldx\tregsave+1")) {
2281 } else if (GetNextCodeLines (L2[12], &L2[13], 1) &&
2282 LineMatch (L2[10], "\tldy\t#$") &&
2283 GetHexNum (L2[10]->Line+7) == Offs &&
2284 LineFullMatch (L2[11], "\tjsr\tstaxysp") &&
2285 LineFullMatch (L2[12], "\tlda\tregsave") &&
2286 LineFullMatch (L2[13], "\tldx\tregsave+1")) {
2299 /* Get a pointer to the last line of the preceding sequence */
2300 L3 = &L2[LinesToRemove-1];
2302 /* Check if AX is actually used following the code above. If not,
2303 * we don't need to load A/X from regsave. Since X will never by
2304 * used without A, check just for A.
2306 NeedLoad = RegAUsed (L3[0]);
2308 /* Replace the ldy instruction, offset must point to the low byte */
2309 sprintf (L->Line+7, "%02X", Offs);
2311 /* Insert the first part of the code */
2312 L = NewLineAfter (L, "\tlda\t(sp),y");
2314 L = NewLineAfter (L, "\tsta\tptr1");
2316 L = NewLineAfter (L, "\tclc");
2317 L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
2318 L = NewLineAfter (L, "\tsta\t(sp),y");
2319 L = NewLineAfter (L, "\tiny");
2320 L = NewLineAfter (L, "\tlda\t(sp),y");
2322 L = NewLineAfter (L, "\tsta\tptr1+1");
2324 L = NewLineAfter (L, "\tadc\t#$00");
2325 L = NewLineAfter (L, "\tsta\t(sp),y");
2327 /* Check if we must really load the old value into a/x or if the
2328 * code may be replaced by something else.
2330 if (GetNextCodeLines (L3[0], &L3[1], 1)) {
2331 if (LineFullMatch (L3[1], "\tjsr\tldaui")) {
2332 /* Load char indirect */
2333 L = NewLineAfter (L, "\tldx\t#$00");
2334 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2337 } else if (LineFullMatch (L3[1], "\tsta\tptr1") &&
2338 GetNextCodeLines (L3[1], &L3[2], 3) &&
2339 LineFullMatch (L3[2], "\tstx\tptr1+1") &&
2340 LineFullMatch (L3[3], "\tldx\t#$00") &&
2341 LineFullMatch (L3[4], "\tlda\t(ptr1,x)")) {
2343 /* Load char indirect, inlined */
2344 L = NewLineAfter (L, "\tldx\t#$00");
2345 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2349 } else if (LineFullMatch (L3[1], "\tjsr\tldaxi")) {
2350 /* Load word indirect */
2351 L = NewLineAfter (L, "\tldy\t#$01");
2352 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2353 L = NewLineAfter (L, "\ttax");
2354 L = NewLineAfter (L, "\tdey");
2355 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2359 } else if (LineFullMatch (L3[1], "\tjsr\tpushax")) {
2360 if (GetNextCodeLines (L3[1], &L3[2], 2) &&
2361 LineMatch (L3[2], "\tlda\t") &&
2362 LineFullMatch (L3[3], "\tjsr\tstaspp")) {
2364 /* Store to pointer */
2365 L = NewLineAfter (L, L3[2]->Line);
2366 L = NewLineAfter (L, "\tldy\t#$00");
2367 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2371 } else if (GetNextCodeLines (L3[1], &L3[2], 3) &&
2372 LineMatch (L3[2], "\tldy\t#$") &&
2373 LineFullMatch (L3[3], "\tlda\t(sp),y") &&
2374 LineFullMatch (L3[4], "\tjsr\tstaspp")) {
2376 /* Beware: We have to correct the stack offset, since we will
2377 * remove the pushax instruction!
2379 sprintf (L3[2]->Line+7, "%02X", GetHexNum (L3[2]->Line+7)-2);
2381 /* Store to pointer */
2382 L = NewLineAfter (L, L3[2]->Line);
2383 L = NewLineAfter (L, L3[3]->Line);
2384 L = NewLineAfter (L, "\tldy\t#$00");
2385 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2394 /* If we need to load a/x, add the code */
2396 L = NewLineAfter (L, "\tlda\tptr1");
2397 L = NewLineAfter (L, "\tldx\tptr1+1");
2400 /* Remove the code that is no longer needed */
2401 FreeLines (L2[0], L2[LinesToRemove-1]);
2403 /* Return the new line and success */
2404 *Start = NextCodeLine (L);
2410 static void OptPtrOps (void)
2411 /* Optimize several pointer and array constructs */
2415 Line* L = FirstCode;
2418 if (OptPtrOps1 (&L)) {
2420 } else if (OptPtrOps2 (&L)) {
2424 /* Search for the following sequence:
2432 * and replace it by:
2439 else if (LineFullMatch (L, "\tlda\tregsave") && /* Match on start */
2440 GetNextCodeLines (L, L2, 4) && /* Fetch next lines */
2441 LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */
2442 LineFullMatch (L2 [1], "\tjsr\tpushax") &&
2443 LineMatch (L2 [2], "\tlda\t#$") &&
2444 LineFullMatch (L2 [3], "\tjsr\tstaspp")) {
2446 /* Found the sequence, replace it */
2447 L = ReplaceLine (L, L2 [2]->Line); /* lda #$.. */
2448 L2 [0] = ReplaceLine (L2 [0], "\tldy\t#$00");
2449 L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
2451 /* Free the remaining lines */
2452 FreeLines (L2 [2], L2 [3]);
2455 /* Search for the following sequence:
2461 * and replace it by:
2467 else if (LineFullMatch (L, "\tlda\tregsave") && /* Match on start */
2468 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
2469 LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */
2470 LineFullMatch (L2 [1], "\tjsr\tldaui")) {
2472 /* Found the sequence, replace it */
2473 L = ReplaceLine (L, "\tldx\t#$00");
2474 L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
2476 /* Free the remaining lines */
2481 * Search for the following sequence:
2490 * and replace it by:
2501 else if (LineFullMatch (L, "\tlda\tregsave") &&
2502 GetNextCodeLines (L, L2, 5) &&
2503 LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
2504 LineFullMatch (L2 [1], "\tjsr\tpushax") &&
2505 LineMatch (L2 [2], "\tldx\t#$") &&
2506 LineMatch (L2 [3], "\tlda\t#$") &&
2507 LineFullMatch (L2 [4], "\tjsr\tstaxspp")) {
2509 /* Found the sequence, replace it */
2510 L = ReplaceLine (L, "\tldy\t#$01");
2511 L2 [0] = ReplaceLine (L2 [0], L2 [2]->Line);
2512 L2 [0]->Line [3] = 'a';
2513 L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
2514 L2 [4] = ReplaceLine (L2 [4], L2 [3]->Line);
2515 L2 [2] = ReplaceLine (L2 [2], "\ttax");
2516 L2 [3] = ReplaceLine (L2 [3], "\tdey");
2517 L = NewLineAfter (L2 [4], "\tsta\t(regsave),y");
2521 * Search for the following sequence:
2530 * and replace it by:
2536 else if (LineFullMatch (L, "\tlda\tregsave") &&
2537 GetNextCodeLines (L, L2, 5) &&
2538 LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
2539 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2540 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2541 LineFullMatch (L2 [3], "\tldx\t#$00") &&
2542 LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
2544 /* Found the sequence, replace it */
2545 L = ReplaceLine (L, "\tldx\t#$00");
2546 L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
2548 /* Remove the remaining lines */
2549 FreeLines (L2 [1], L2 [4]);
2552 /* Search for the following sequence:
2558 * and replace it by:
2567 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2568 GetNextCodeLines (L, L2, 2) &&
2569 LineMatch (L2 [0], "\tlda\t") &&
2570 LineFullMatch (L2 [1], "\tjsr\tstaspp")) {
2572 /* Found the sequence, replace it */
2573 L = ReplaceLine (L, "\tsta\tptr1");
2574 L2 [1] = ReplaceLine (L2 [1], L2 [0]->Line); /* lda ... */
2575 L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
2576 L2 [2] = NewLineAfter (L2 [1], "\tldy\t#$00");
2577 L = NewLineAfter (L2 [2], "\tsta\t(ptr1),y");
2580 /* Search for the following sequence:
2587 * and replace it by:
2596 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2597 GetNextCodeLines (L, L2, 3) &&
2598 LineMatch (L2 [0], "\tlda\t") &&
2599 LineMatch (L2 [1], "\tldy\t#$") &&
2600 LineFullMatch (L2 [2], "\tjsr\tstaspidx")) {
2602 /* Found the sequence, replace it */
2603 L = ReplaceLine (L, "\tsta\tptr1");
2604 L = NewLineAfter (L, "\tstx\tptr1+1");
2605 L2 [2] = ReplaceLine (L2 [2], "\tsta\t(ptr1),y");
2608 /* Search for the following sequence:
2615 * and replace it by:
2624 * Beware: Since we remove a call to a function that changes the stack
2625 * pointer, we have to adjust the stack address for the lda.
2628 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2629 GetNextCodeLines (L, L2, 3) &&
2630 LineMatch (L2 [0], "\tldy\t#$") &&
2631 LineFullMatch (L2 [1], "\tlda\t(sp),y") &&
2632 LineFullMatch (L2 [2], "\tjsr\tstaspp")) {
2634 /* Found the sequence, replace it. First create a new load
2635 * instruction for the changed stack offset.
2638 sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
2639 L = ReplaceLine (L, "\tsta\tptr1");
2640 L2 [1] = ReplaceLine (L2 [1], Buf); /* ldy ... */
2641 L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
2642 L2 [2] = ReplaceLine (L2 [2], "\tlda\t(sp),y");
2643 L2 [3] = NewLineAfter (L2 [2], "\tldy\t#$00");
2644 L = NewLineAfter (L2 [3], "\tsta\t(ptr1),y");
2647 /* Search for the following sequence:
2655 * and replace it by:
2664 * Beware: Since we remove a call to a function that changes the stack
2665 * pointer, we have to adjust the stack address for the lda.
2668 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2669 GetNextCodeLines (L, L2, 4) &&
2670 LineMatch (L2 [0], "\tldy\t#$") &&
2671 LineFullMatch (L2 [1], "\tlda\t(sp),y") &&
2672 LineMatch (L2 [2], "\tldy\t#$") &&
2673 LineFullMatch (L2 [3], "\tjsr\tstaspidx")) {
2675 /* Found the sequence, replace it. First create a new load
2676 * instruction for the changed stack offset.
2679 sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
2680 L = ReplaceLine (L, "\tsta\tptr1");
2681 L = NewLineAfter (L, "\tstx\tptr1+1");
2682 L2 [0] = ReplaceLine (L2 [0], Buf); /* ldy ... */
2683 L2 [3] = ReplaceLine (L2 [3], "\tsta\t(ptr1),y");
2686 /* Search for the following sequence:
2699 * and replace it by:
2707 * The load of X may be omitted if X is not used below.
2709 else if (LineMatch (L, "\tldax\t_") &&
2710 GetNextCodeLines (L, L2, 9) &&
2711 LineMatch (L2 [0], "\tldy\t#$") &&
2712 LineFullMatch (L2 [1], "\tclc") &&
2713 LineFullMatch (L2 [2], "\tadc\t(sp),y") &&
2714 LineFullMatch (L2 [3], "\tbcc\t*+3") &&
2715 LineFullMatch (L2 [4], "\tinx") &&
2716 LineFullMatch (L2 [5], "\tsta\tptr1") &&
2717 LineFullMatch (L2 [6], "\tstx\tptr1+1") &&
2718 LineFullMatch (L2 [7], "\tldx\t#$00") &&
2719 LineFullMatch (L2 [8], "\tlda\t(ptr1,x)")) {
2721 /* Found the sequence, replace it */
2723 strcpy (Label, L->Line + 6); /* Remember the label */
2724 L = ReplaceLine (L, L2 [0]->Line); /* ldy .. */
2725 L = NewLineAfter (L, "\tlda\t(sp),y");
2726 L = NewLineAfter (L, "\ttay");
2727 if (RegXUsed (L2[8])) {
2728 L = NewLineAfter (L, "\tldx\t#$00");
2730 L = NewLineAfter (L, "\tlda\t%s,y", Label);
2732 /* Remove the remaining stuff. There may be hints between the
2733 * instructions, remove them too
2735 FreeLines (L2[0], L2 [8]);
2759 * This change will cost 2 cycles, but it saves a lot of code (6 bytes
2760 * per occurrence), so we will accept the overhead. It may even be
2761 * possible to rewrite the library routine to get rid of the additional
2764 else if (LineMatch (L, "\tldy\t#$") &&
2765 GetNextCodeLines (L, L2, 6) &&
2766 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
2767 LineFullMatch (L2 [1], "\ttax") &&
2768 LineFullMatch (L2 [2], "\tdey") &&
2769 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
2770 LineMatch (L2 [4], "\tldy\t#$") &&
2771 LineFullMatch (L2 [5], "\tjsr\tldauidx")) {
2773 /* Found - replace it */
2774 L2 [4]->Line [3] = 'x'; /* Change to ldx */
2775 if (LineFullMatch (L, "\tldy\t#$01")) {
2776 /* Word at offset zero */
2778 L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp");
2780 ReplaceLine (L2 [5], "\tjsr\tldauiysp");
2783 /* Delete the remaining lines */
2784 FreeLines (L2 [0], L2 [3]);
2808 * This change will has an overhead of 10 cycles, but it saves 11(!)
2809 * bytes per invocation. Maybe we should apply only if FavourSize is
2812 else if (LineMatch (L, "\tldy\t#$") &&
2813 GetNextCodeLines (L, L2, 8) &&
2814 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
2815 LineFullMatch (L2 [1], "\ttax") &&
2816 LineFullMatch (L2 [2], "\tdey") &&
2817 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
2818 LineFullMatch (L2 [4], "\tsta\tptr1") &&
2819 LineFullMatch (L2 [5], "\tstx\tptr1+1") &&
2820 LineFullMatch (L2 [6], "\tldx\t#$00") &&
2821 LineFullMatch (L2 [7], "\tlda\t(ptr1,x)")) {
2823 /* Found - replace it */
2824 if (LineFullMatch (L, "\tldy\t#$01")) {
2825 /* Word at offset zero */
2827 L = ReplaceLine (L2 [0], "\tjsr\tldau00sp");
2829 ReplaceLine (L2 [0], "\tjsr\tldau0ysp");
2832 /* Delete the remaining lines */
2833 FreeLines (L2 [1], L2 [7]);
2855 * Provided that X is not used later.
2857 else if (LineMatch (L, "\tldy\t#$") &&
2858 GetNextCodeLines (L, L2, 6) &&
2859 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
2860 LineFullMatch (L2 [1], "\ttax") &&
2861 LineFullMatch (L2 [2], "\tdey") &&
2862 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
2863 Is16BitStore (L2[4], L2[5]) &&
2864 !RegXUsed (L2[5])) {
2866 /* Found - replace it */
2867 L2[1] = ReplaceLine (L2[1], L2[5]->Line);
2868 L2[1]->Line[3] = 'a';
2870 /* Delete the remaining lines */
2873 /* Start over at last line */
2878 L = NextCodeLine (L);
2884 static void OptRegVars (void)
2885 /* Optimize register variable uses */
2889 Line* L = FirstCode;
2892 /* Search for the following sequence:
2898 * and replace it by:
2904 if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2905 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
2906 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2907 LineFullMatch (L2 [1], "\tjsr\tldaui") &&
2908 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2909 strcmp (L2 [0]->Line + 14, "+1") == 0) {
2912 sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
2914 /* Found the sequence, replace it */
2915 L = ReplaceLine (L, "\tldx\t#$00");
2916 L2 [0] = ReplaceLine (L2 [0], Buf);
2918 /* Free the remaining lines */
2922 /* Search for the following sequence:
2931 * and replace it by:
2937 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2938 GetNextCodeLines (L, L2, 5) && /* Fetch next lines */
2939 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2940 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2941 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2942 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2943 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2944 LineFullMatch (L2 [3], "\tldx\t#$00") &&
2945 LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
2948 sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
2950 /* Found the sequence, replace it */
2951 L = ReplaceLine (L, "\tldx\t#$00");
2952 L2 [0] = ReplaceLine (L2 [0], Buf);
2954 /* Free the remaining lines */
2955 FreeLines (L2 [1], L2 [4]);
2958 /* Search for the following sequence:
2965 * and replace it by:
2972 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2973 GetNextCodeLines (L, L2, 3) && /* Fetch next lines */
2974 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2975 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2976 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2977 LineMatch (L2 [1], "\tldy\t#$") &&
2978 LineFullMatch (L2 [2], "\tjsr\tldauidx")) {
2981 sprintf (Buf, "\tlda\t(%s),y", L->Line + 5);
2983 /* Found the sequence, replace it */
2984 L = ReplaceLine (L, L2 [1]->Line);
2985 L2 [0] = ReplaceLine (L2 [0], "\tldx\t#$00");
2986 L2 [1] = ReplaceLine (L2 [1], Buf);
2988 /* Free the remaining lines */
2992 /* Search for the following sequence:
3002 * and replace it by:
3008 * The source form is not generated by the parser but by the optimizer.
3010 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
3011 GetNextCodeLines (L, L2, 6) && /* Fetch next lines */
3012 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
3013 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
3014 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
3015 LineFullMatch (L2 [1], "\tsta\tptr1") &&
3016 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
3017 LineMatch (L2 [3], "\tlda\t") &&
3018 LineMatch (L2 [4], "\tldy\t#$") &&
3019 LineMatch (L2 [5], "\tsta\t(ptr1),y")) {
3022 sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
3024 /* Found the sequence, replace it */
3025 L2 [5] = ReplaceLine (L2 [5], Buf);
3027 /* Free the remaining lines */
3028 FreeLines (L, L2 [2]);
3030 /* Make the line pointer valid again */
3034 /* Search for the following sequence:
3045 * and replace it by:
3052 * The source form is not generated by the parser but by the optimizer.
3054 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
3055 GetNextCodeLines (L, L2, 7) && /* Fetch next lines */
3056 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
3057 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
3058 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
3059 LineFullMatch (L2 [1], "\tsta\tptr1") &&
3060 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
3061 LineMatch (L2 [3], "\tldy\t#$") &&
3062 LineFullMatch (L2 [4], "\tlda\t(sp),y") &&
3063 LineMatch (L2 [5], "\tldy\t#$") &&
3064 LineMatch (L2 [6], "\tsta\t(ptr1),y")) {
3067 sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
3069 /* Found the sequence, replace it */
3070 L2 [6] = ReplaceLine (L2 [6], Buf);
3072 /* Free the remaining lines */
3073 FreeLines (L, L2 [2]);
3075 /* Make the line pointer valid again */
3080 L = NextCodeLine (L);
3086 static void OptDoubleJumps (void)
3087 /* Remove/rearrange jumps that jump to other jumps */
3089 static const char* Jumps [] = {
3100 Line* L = FirstCode;
3105 /* Is this a jump? */
3106 while ((I = LineMatchX (L, Jumps)) >= 0) {
3108 /* Yes. Get the target label */
3109 Line* Target = GetTargetLine (L->Line + 5);
3111 /* Target points to the label itself. Skip lines until we reach
3112 * one that is not a label.
3114 Target = NextInstruction (Target);
3116 /* Be sure, this line is not the same as the one the jump is
3117 * in (this happens if there is an empty loop).
3123 if (LineMatch (Target, "\tjmp\t")) {
3125 /* The target is itself a jump. If this is a short branch, get
3126 * the final target and check if it is in reach. Bail out if
3129 if (L->Line[1] == 'b') {
3130 Line* FinalTarget = GetTargetLine (Target->Line+5);
3131 FinalTarget = NextInstruction (FinalTarget);
3132 if ((D = GetJumpDistance (L, FinalTarget)) >= 123) {
3137 /* Make sure the jump does indeed point to another label.
3138 * It may happen that this is not the case for some endless
3139 * loop (while(1) and similar).
3141 if (strcmp (L->Line+5, Target->Line+5) == 0) {
3142 /* Same label, bail out */
3146 /* Use the label in the original jump instead */
3147 L = ReplaceLine (L, "%.5s%s", L->Line, Target->Line+5);
3149 } else if (I < 2 && LineMatch (Target, Jumps [I])) {
3151 /* Conditional jump. Use final label */
3152 strcpy (L->Line+5, Target->Line+5);
3160 L = NextCodeLine (L);
3166 static void OptJumpRTS (void)
3167 /* Replace jumps to an RTS by an RTS */
3169 Line* L = FirstCode;
3171 /* Is this a jump to a numbered label? */
3172 if (LineMatch (L, "\tjmp\t") && L->Line [5] == 'L' && IsDigit (L->Line [6])) {
3174 /* Yes. Get the target label */
3175 Line* Target = GetTargetLine (L->Line+5);
3177 /* Target points to the label itself. Get the next line */
3178 Target = NextCodeLine (Target);
3179 if (LineFullMatch (Target, "\trts")) {
3180 /* Replace the jump by an RTS */
3181 L = ReplaceLine (L, "\trts");
3184 L = NextCodeLine (L);
3190 static void OptBoolTransforms (void)
3191 /* Try to remove the boolean transformation subroutines where they aren't
3197 const char* BranchTarget;
3199 Line* L = FirstCode;
3202 /* Search for a boolean transformer followed by a conditional jump. */
3203 if (LineMatch (L, "\tjsr\tbool") &&
3204 GetNextCodeLines (L, L2, 1) &&
3205 IsCondJump (L2 [0])) {
3207 /* Make the boolean transformer unnecessary by changing the
3208 * the conditional jump to evaluate the condition flags that
3209 * are set after the compare directly. Note: jeq jumps if
3210 * the condition is not met, jne jumps if the condition is met.
3213 /* Get the condition code */
3214 int Cond = FindCond (L->Line + 9);
3220 /* Invert the code if we jump on condition not met. */
3221 if (L2[0]->Line [2] == 'e' && L2[0]->Line [3] == 'q') {
3222 /* Jumps if condition false, invert condition */
3223 Cond = CmpInvertTab [Cond];
3226 /* For easier reading, get a pointer to the jump target */
3227 BranchTarget = L2[0]->Line+5;
3229 /* Check if we can replace the jump (sometimes we would need two
3230 * conditional jumps, we will not handle that for now since it
3231 * has some complications - both jumps may be far jumps for
3232 * example making the jumps more costly than the bool transformer
3233 * subroutine). If we cannot replace the jump, bail out.
3238 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3242 L = ReplaceLine (L, "\tjne\t%s", BranchTarget);
3246 Label = AllocLabel ();
3247 L = ReplaceLine (L, "\tbeq\tL%04X", Label);
3248 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3249 L = NewLabelAfter(L, Label);
3253 L = ReplaceLine (L, "\tjpl\t%s", BranchTarget);
3257 L = ReplaceLine (L, "\tjmi\t%s", BranchTarget);
3261 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3262 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3266 Label = AllocLabel ();
3267 L = ReplaceLine (L, "\tbeq\tL%04X", Label);
3268 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3269 L = NewLabelAfter(L, Label);
3273 L = ReplaceLine (L, "\tjcs\t%s", BranchTarget);
3277 L = ReplaceLine (L, "\tjcc\t%s", BranchTarget);
3281 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3282 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3286 Internal ("Unknown jump condition: %u", Cond);
3290 /* Remove the old stuff */
3296 L = NextCodeLine (L);
3302 static void OptCompares2 (void)
3303 /* Try to optimize the integer compare subroutines. */
3307 const char* BranchTarget;
3310 Line* L = FirstCode;
3328 if (LineMatch (L, "\tlda\t") &&
3329 GetNextCodeLines (L, L2, 5) &&
3330 IsLoadAX (L, L2[0]) &&
3331 LineFullMatch (L2[1], "\tcpx\t#$00") &&
3332 LineFullMatch (L2[2], "\tbne\t*+4") &&
3333 LineFullMatch (L2[3], "\tcmp\t#$00") &&
3334 IsCondJump (L2[4])) {
3336 /* Replace the load of X by an ora */
3337 L2[0]->Line[1] = 'o';
3338 L2[0]->Line[2] = 'r';
3339 L2[0]->Line[3] = 'a';
3341 /* Remove unneeded stuff */
3342 FreeLines (L2[1], L2[3]);
3346 /* Same for local variables: Replace
3366 else if (LineMatch (L, "\tldy\t#$") &&
3367 GetNextCodeLines (L, L2, 8) &&
3368 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
3369 LineFullMatch (L2[1], "\ttax") &&
3370 LineFullMatch (L2[2], "\tdey") &&
3371 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
3372 LineFullMatch (L2[4], "\tcpx\t#$00") &&
3373 LineFullMatch (L2[5], "\tbne\t*+4") &&
3374 LineFullMatch (L2[6], "\tcmp\t#$00") &&
3375 IsCondJump (L2[7])) {
3377 /* Replace the second load by an ora */
3378 L2[3]->Line[1] = 'o';
3379 L2[3]->Line[2] = 'r';
3380 L2[3]->Line[3] = 'a';
3382 /* Remove unneeded stuff */
3384 FreeLines (L2[4], L2[6]);
3388 /* Search for the call to a compare subroutine followed by a
3391 else if (LineMatch (L, "\tjsr\ttos") &&
3392 (L2[0] = NextCodeLine (L)) != 0 &&
3393 IsCondJump (L2[0])) {
3395 /* Extract the condition from the function name and branch */
3396 C = CheckAndGetIntCmp (L, L2[0]);
3398 /* Something is wrong */
3402 /* Replace the subroutine call by a cheaper one */
3403 L = ReplaceLine (L, "\tjsr\ttosicmp");
3405 /* For easier reading, get a pointer to the jump target */
3406 BranchTarget = L2[0]->Line+5;
3408 /* Check if we can replace the jump (sometimes we would need two
3409 * conditional jumps, we will not handle that for now since it
3410 * has some complications - both jumps may be far jumps for
3411 * example making the jumps more costly than the bool transformer
3412 * subroutine). If we cannot replace the jump, bail out.
3417 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3421 L = NewLineAfter (L, "\tjne\t%s", BranchTarget);
3425 Label = AllocLabel ();
3426 L = NewLineAfter (L, "\tbeq\tL%04X", Label);
3427 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3428 L = NewLabelAfter(L, Label);
3432 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3436 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3440 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3441 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3445 Label = AllocLabel ();
3446 L = NewLineAfter (L, "\tbeq\tL%04X", Label);
3447 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3448 L = NewLabelAfter(L, Label);
3452 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3456 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3460 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3461 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3465 Internal ("Unknown jump condition: %u", C);
3469 /* Remove the old stuff */
3474 L = NextCodeLine (L);
3480 static void OptTests (void)
3481 /* Remove unnecessary tests */
3485 static const char* BitOps [] = {
3492 /* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
3499 Line* L = FirstCode;
3502 /* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
3509 if ((LineMatch (L, "\tlda\t") ||
3510 LineMatch (L, "\tand\t") ||
3511 LineMatch (L, "\tora\t") ||
3512 LineMatch (L, "\teor\t")) &&
3513 GetNextCodeLines (L, L2, 2) &&
3514 (LineFullMatch (L2 [0], "\ttay") ||
3515 LineFullMatch (L2 [0], "\tcmp\t#$00")) &&
3516 IsCondJump (L2 [1])) {
3518 /* We can remove the tay */
3529 * and remove the tax.
3531 else if (LineMatchX (L, BitOps) >= 0 &&
3532 GetNextCodeLines (L, L2, 2) &&
3533 LineFullMatch (L2[0], "\ttax") &&
3534 IsCondJump (L2[1])) {
3536 /* Remove the tax including a hint line of there is one */
3537 if (LineFullMatch (L2[0]->Prev, "+forcetest")) {
3538 FreeLine (L2[0]->Prev);
3542 /* If the line before L loads X, this is useless and may be removed */
3543 L2[0] = PrevCodeLine (L);
3544 if (LineFullMatch (L2[0], "\tldx\t#$00")) {
3550 /* Search for the sequence
3561 else if (LineMatch (L, "\tstx\t") &&
3562 GetNextCodeLines (L, L2, 2) &&
3563 LineFullMatch (L2[0], "\tstx\ttmp1") &&
3564 LineFullMatch (L2[1], "\tora\ttmp1")) {
3566 /* Found, replace it */
3567 L = NewLineAfter (L, "\tora\t%s", L->Line+5);
3569 /* Remove remaining stuff */
3570 FreeLines (L2[0], L2[1]);
3576 L = NextCodeLine (L);
3582 static void OptBitOps (void)
3583 /* Optimize bit oeprations */
3587 /* Walk over the code */
3588 Line* L = FirstCode;
3594 * and #$yy ; adc/eor/ora
3603 * While this saves nothing here, it transforms the code to contain an
3604 * explicit register load that may be removed by the basic block
3605 * optimization later. As a special optimization for the 65C02, the
3606 * "ora" and "and" ops may be replaced by "trb" and "tsb" resp. if the
3607 * value in A is not used later.
3609 if (LineMatch (L, "\tlda\t") &&
3610 L->Line[5] != '#' &&
3611 GetNextCodeLines (L, L2, 2) &&
3612 LineMatch (L2[1], "\tsta\t") &&
3613 strcmp (L->Line+5, L2[1]->Line+5) == 0) {
3615 if (LineMatch (L2[0], "\tand\t#$")) {
3617 unsigned Val = GetHexNum (L2[0]->Line+7);
3620 /* AND with 0x00, remove the mem access */
3624 /* Replace the AND by a load */
3625 L = ReplaceLine (L2[0], "\tlda\t#$%02X", Val);
3627 } else if (Val == 0xFF) {
3629 /* AND with 0xFF, just load the value from memory */
3630 FreeLines (L2[0], L2[1]);
3632 } else if (CPU == CPU_65C02 &&
3635 !RegAUsed (L2[1])) {
3637 /* Replace by trb */
3638 ReplaceLine (L, "\tlda\t#$%02X", (~Val) & 0xFF);
3639 L = ReplaceLine (L2[0], "\ttrb\t%s", L2[1]->Line+5);
3645 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 L = 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 L = ReplaceLine (L2[0], "\ttsb\t%s", L2[1]->Line+5);
3678 ReplaceLine (L, "\tlda\t#$%02X", Val);
3679 ReplaceLine (L2[0], "\tora\t%s", L2[1]->Line+5);
3684 } else if (LineMatch (L2[0], "\teor\t#$") ||
3685 LineMatch (L2[0], "\tadc\t#$")) {
3688 L = ReplaceLine (L, "\tlda\t%s", L2[0]->Line+5);
3689 ReplaceLine (L2[0], "\t%.3s\t%s", L2[0]->Line+1, L2[1]->Line+5);
3695 L = NextCodeLine (L);
3701 static void OptNeg (void)
3702 /* Optimize the "bnegax/jeq" and "bnegax/jne" sequences */
3706 Line* L = FirstCode;
3709 /* Search for the sequence:
3715 * and replace it by:
3720 if (LineMatch (L, "\tlda\t") && /* Match on start */
3721 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
3722 LineFullMatch (L2 [0], "\tjsr\tbnega") &&
3723 IsCondJump (L2 [1])) {
3725 /* Found the sequence, replace it */
3727 InvertZJump (L2 [1]);
3731 /* Search for the sequence:
3749 else if (LineMatch (L, "\tldy\t#$") &&
3750 GetNextCodeLines (L, L2, 6) &&
3751 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
3752 LineFullMatch (L2[1], "\ttax") &&
3753 LineFullMatch (L2[2], "\tdey") &&
3754 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
3755 LineFullMatch (L2[4], "\tjsr\tbnegax") &&
3756 IsCondJump (L2[5])) {
3758 L2[1] = ReplaceLine (L2[1], "\tdey");
3759 L2[2] = ReplaceLine (L2[2], "\tora\t(sp),y");
3760 FreeLines (L2[3], L2[4]);
3761 InvertZJump (L2[5]);
3765 /* Search for the sequence:
3778 else if (LineMatch (L, "\tlda\t") &&
3779 GetNextCodeLines (L, L2, 3) &&
3780 IsLoadAX (L, L2[0]) &&
3781 LineFullMatch (L2[1], "\tjsr\tbnegax") &&
3782 IsCondJump (L2[2])) {
3784 /* Replace the load of X by ora */
3785 L2[0]->Line[1] = 'o';
3786 L2[0]->Line[2] = 'r';
3787 L2[0]->Line[3] = 'a';
3789 InvertZJump (L2[2]);
3793 /* Search for the sequence:
3799 * and replace it by:
3805 else if (LineMatch (L, "\tjsr\t_") && /* Match on start */
3806 GetNextCodeLines (L, L2, 2) &&
3807 LineMatch (L2 [0], "\tjsr\tbnega") &&
3808 IsCondJump (L2 [1])) {
3810 if (LineFullMatch (L2 [0], "\tjsr\tbnega")) {
3812 L2 [0] = ReplaceLine (L2 [0], "\ttax"); /* Test a */
3815 L2 [0] = ReplaceLine (L2 [0], "\tstx\ttmp1");
3816 NewLineAfter (L2 [0], "\tora\ttmp1");
3819 /* Invert the jump */
3820 InvertZJump (L2 [1]);
3825 L = NextCodeLine (L);
3831 static void OptTriples (void)
3832 /* Replace code triples */
3834 static const char* Pat1 [] = {
3842 static const char* Pat2 [] = {
3850 static const char* Replace [] = {
3858 Line* L = FirstCode;
3860 int I = LineFullMatchX (L, Pat1);
3862 /* We found the first match, get the next line */
3863 Line* L2 = NextCodeLine (L);
3864 if (L2 && LineFullMatch (L2, Pat2 [I])) {
3865 /* Found. Replace by the short call */
3867 L = ReplaceLine (L, Replace [I]);
3871 L = NextCodeLine (L);
3877 static Line* OptOneBlock (Line* L)
3878 /* Optimize the register contents inside one basic block */
3880 static const char* Compares [] = {
3881 "\tjsr\ttoseq00", "\tjsr\ttoseqa0", "\tjsr\ttoseqax",
3882 "\tjsr\ttoseqeax", "\tjsr\ttosne00", "\tjsr\ttosnea0",
3883 "\tjsr\ttosneax", "\tjsr\ttosneeax", "\tjsr\ttoslt00",
3884 "\tjsr\ttoslta0", "\tjsr\ttosltax", "\tjsr\ttosult00",
3885 "\tjsr\ttosulta0", "\tjsr\ttosultax", "\tjsr\ttoslteax",
3886 "\tjsr\ttosulteax", "\tjsr\ttosle00", "\tjsr\ttoslea0",
3887 "\tjsr\ttosleax", "\tjsr\ttosule00", "\tjsr\ttosulea0",
3888 "\tjsr\ttosuleax", "\tjsr\ttosleeax", "\tjsr\ttosuleeax",
3889 "\tjsr\ttosgt00", "\tjsr\ttosgta0", "\tjsr\ttosgtax",
3890 "\tjsr\ttosugt00", "\tjsr\ttosugta0", "\tjsr\ttosugtax",
3891 "\tjsr\ttosgteax", "\tjsr\ttosugteax", "\tjsr\ttosge00",
3892 "\tjsr\ttosgea0", "\tjsr\ttosgeax", "\tjsr\ttosuge00",
3893 "\tjsr\ttosugea0", "\tjsr\ttosugeax", "\tjsr\ttosgeeax",
3898 static const char* MakeBool [] = {
3899 "\tjsr\tbooleq", "\tjsr\tboolne", "\tjsr\tboollt",
3900 "\tjsr\tboolle", "\tjsr\tboolgt", "\tjsr\tboolge",
3901 "\tjsr\tboolult", "\tjsr\tboolule", "\tjsr\tboolugt",
3906 int A = -1; /* Contents of A register */
3907 int X = -1; /* Contents of X register */
3908 int Y = -1; /* Contents of Y register */
3913 while (L && !IsLabel (L)) {
3915 /* Handle all instructions. All instructions not tested here have
3916 * no effects on the register contents.
3919 if (L->Line [0] == '+') {
3920 /* This is a hint */
3921 if (LineMatch (L, "+a:")) {
3922 /* Information about a */
3923 switch (L->Line [3]) {
3924 case '!': A = -1; break;
3925 case '=': A = GetHexNum (L->Line + 4); break;
3927 } else if (LineMatch (L, "+x:")) {
3928 /* The code generator tells something about the x register */
3929 switch (L->Line [3]) {
3930 case '!': X = -1; break;
3931 case '=': X = GetHexNum (L->Line + 4); break;
3933 } else if (LineMatch (L, "+y:")) {
3934 /* Information about the y register */
3935 switch (L->Line [3]) {
3936 case '!': Y = -1; break;
3937 case '=': Y = GetHexNum (L->Line + 4); break;
3940 } else if (LineMatch (L, "\tadc\t")) {
3941 if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
3942 L->Line[strlen(L->Line)-2] = '\0';
3945 } else if (LineMatch (L, "\tand\t")) {
3947 } else if (LineFullMatch (L, "\tasl\ta")) {
3949 A = (A << 1) & 0xFF;
3951 } else if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
3952 L->Line[strlen(L->Line)-2] = '\0';
3953 } else if (CPU == CPU_65C02 && (LineFullMatch (L, "\tdea") ||
3954 LineFullMatch (L, "\tdec\ta"))) {
3956 } else if (LineFullMatch (L, "\tdex")) {
3958 } else if (LineFullMatch (L, "\tdey")) {
3960 } else if (LineMatch (L, "\teor")) {
3962 } else if (CPU == CPU_65C02 && (LineFullMatch (L, "\tina") ||
3963 LineFullMatch (L, "\tinc\ta"))) {
3965 } else if (LineFullMatch (L, "\tinx")) {
3967 } else if (LineFullMatch (L, "\tiny")) {
3969 } else if (LineFullMatch (L, "\tjsr\taddeq0sp")) {
3970 /* We know about this function */
3973 } else if (LineFullMatch (L, "\tjsr\taddeqysp")) {
3974 /* We know about this function */
3977 } else if (LineFullMatch (L, "\tjsr\taxulong")) {
3978 /* We know about this function and we're trying to replace it by
3979 * inline code if we have already a register that contains zero.
3984 } else if (X == 0) {
3986 } else if (Y == 0) {
3992 /* We cannot replace the code, but we know about the results */
3995 L = ReplaceLine (L, "\tst%c\tsreg", C);
3996 NewLineAfter (L, "\tst%c\tsreg+1", C);
3998 } else if (LineFullMatch (L, "\tjsr\tbnega")) {
3999 /* We know about this function */
4002 } else if (LineFullMatch (L, "\tjsr\tbnegax")) {
4003 /* We know about this function */
4006 } else if (LineFullMatch (L, "\tjsr\tbnegeax")) {
4007 /* We know about this function */
4010 } else if (LineFullMatch (L, "\tjsr\tcomplax")) {
4011 /* We know about this function */
4018 } else if (LineFullMatch (L, "\tjsr\tdecax1")) {
4019 /* We know about this function */
4021 } else if (LineFullMatch (L, "\tjsr\tdecax2")) {
4022 /* We know about this function */
4024 } else if (LineFullMatch (L, "\tjsr\tdecaxy")) {
4025 /* We know about this function */
4027 } else if (LineFullMatch (L, "\tjsr\tdeceaxy")) {
4028 /* We know about this function */
4030 } else if (LineFullMatch (L, "\tjsr\tincax1")) {
4031 /* We know about this function */
4033 } else if (LineFullMatch (L, "\tjsr\tincax2")) {
4034 /* We know about this function */
4036 } else if (LineFullMatch (L, "\tjsr\tinceaxy")) {
4037 /* We know about this function */
4039 } else if (LineFullMatch (L, "\tjsr\tladdeq")) {
4040 /* We know about this function */
4043 } else if (LineFullMatch (L, "\tjsr\tladdeqb")) {
4044 /* We know about this function */
4047 } else if (LineFullMatch (L, "\tjsr\tlbneg")) {
4048 /* We know about this function */
4051 } else if (LineFullMatch (L, "\tjsr\tldai")) {
4052 /* We know about this function */
4055 } else if (LineFullMatch (L, "\tjsr\tldaidx")) {
4056 /* We know about this function */
4058 } else if (LineFullMatch (L, "\tjsr\tldau00sp")) {
4059 /* We know about this function */
4063 } else if (LineFullMatch (L, "\tjsr\tldau0ysp")) {
4064 /* We know about this function */
4068 } else if (LineFullMatch (L, "\tjsr\tldaui")) {
4069 /* We know about this function */
4073 } else if (LineFullMatch (L, "\tjsr\tldaui0sp")) {
4077 } else if (LineFullMatch (L, "\tjsr\tldauidx")) {
4078 /* We know about this function */
4081 } else if (LineFullMatch (L, "\tjsr\tldauiysp")) {
4082 /* We know about this function */
4086 } else if (LineFullMatch (L, "\tjsr\tldax0sp")) {
4087 /* We know about this function */
4090 } else if (LineFullMatch (L, "\tjsr\tldaxi")) {
4091 /* We know about this function */
4094 } else if (LineFullMatch (L, "\tjsr\tldaxidx")) {
4095 /* We know about this function */
4098 } else if (LineFullMatch (L, "\tjsr\tldaxysp")) {
4099 /* We know about this function */
4102 } else if (LineFullMatch (L, "\tjsr\tldeaxi")) {
4103 /* We know about this function */
4106 } else if (LineFullMatch (L, "\tjsr\tldeaxidx")) {
4107 /* We know about this function */
4110 } else if (LineFullMatch (L, "\tjsr\tlsubeq")) {
4111 /* We know about this function */
4114 } else if (LineFullMatch (L, "\tjsr\tlsubeqb")) {
4115 /* We know about this function */
4118 } else if (LineFullMatch (L, "\tjsr\tnegax")) {
4119 /* We know about this function */
4121 } else if (LineFullMatch (L, "\tjsr\tnegeax")) {
4122 /* We know about this function */
4124 } else if (LineFullMatch (L, "\tjsr\tpush0")) {
4125 /* We know about this function */
4129 } else if (LineFullMatch (L, "\tjsr\tpush1")) {
4130 /* We know about this function */
4134 } else if (LineFullMatch (L, "\tjsr\tpush2")) {
4135 /* We know about this function */
4139 } else if (LineFullMatch (L, "\tjsr\tpush3")) {
4140 /* We know about this function */
4144 } else if (LineFullMatch (L, "\tjsr\tpush4")) {
4145 /* We know about this function */
4149 } else if (LineFullMatch (L, "\tjsr\tpush5")) {
4150 /* We know about this function */
4154 } else if (LineFullMatch (L, "\tjsr\tpush6")) {
4155 /* We know about this function */
4159 } else if (LineFullMatch (L, "\tjsr\tpush7")) {
4160 /* We know about this function */
4164 } else if (CPU == CPU_65C02 && LineFullMatch (L, "\tjsr\tpusha")) {
4165 /* We know about this function */
4167 } else if (LineFullMatch (L, "\tjsr\tpusha0")) {
4168 /* We know about this function
4169 * If X is already zero, we may call pushax instead and save two
4173 L = ReplaceLine (L, "\tjsr\tpushax");
4177 } else if (LineFullMatch (L, "\tjsr\tpushax")) {
4178 /* We know about this function */
4180 } else if (LineFullMatch (L, "\tjsr\tpushaysp")) {
4181 /* We know about this function */
4184 } else if (LineFullMatch (L, "\tjsr\tpushc0")) {
4185 /* We know about this function */
4188 } else if (LineFullMatch (L, "\tjsr\tpushc1")) {
4189 /* We know about this function */
4192 } else if (LineFullMatch (L, "\tjsr\tpushc2")) {
4193 /* We know about this function */
4196 } else if (LineFullMatch (L, "\tjsr\tpushw")) {
4197 /* We know about this function (calls pushax) */
4200 } else if (LineFullMatch (L, "\tjsr\tpushw0sp")) {
4201 /* We know about this function(calls pushax) */
4204 } else if (LineFullMatch (L, "\tjsr\tpushwidx")) {
4205 /* We know about this function (calls pushax) */
4208 } else if (LineFullMatch (L, "\tjsr\tpushwysp")) {
4209 /* We know about this function (calls pushax) */
4212 } else if (LineFullMatch (L, "\tjsr\tresteax")) {
4213 /* We know about this function */
4215 } else if (LineFullMatch (L, "\tjsr\tsaveeax")) {
4216 /* We know about this function */
4217 /* Changes nothing */
4218 } else if (LineFullMatch (L, "\tjsr\tshrax1")) {
4219 /* We know about this function */
4221 } else if (LineFullMatch (L, "\tjsr\tshrax2")) {
4222 /* We know about this function */
4224 } else if (LineFullMatch (L, "\tjsr\tshrax3")) {
4225 /* We know about this function */
4227 } else if (LineFullMatch (L, "\tjsr\tshreax1")) {
4228 /* We know about this function */
4230 } else if (LineFullMatch (L, "\tjsr\tshreax2")) {
4231 /* We know about this function */
4233 } else if (LineFullMatch (L, "\tjsr\tshreax3")) {
4234 /* We know about this function */
4236 } else if (LineFullMatch (L, "\tjsr\tstaspp")) {
4237 /* We know about this function */
4239 } else if (LineFullMatch (L, "\tjsr\tstaxspp")) {
4240 /* We know about this function */
4242 } else if (LineFullMatch (L, "\tjsr\tstax0sp")) {
4243 /* We know about this function */
4245 } else if (LineFullMatch (L, "\tjsr\tstaxysp")) {
4246 /* We know about this function */
4248 } else if (LineFullMatch (L, "\tjsr\tsubeq0sp")) {
4249 /* We know about this function */
4252 } else if (LineFullMatch (L, "\tjsr\tsubeqysp")) {
4253 /* We know about this function */
4256 } else if (LineFullMatch (L, "\tjsr\ttosadda0")) {
4257 /* We know about this function */
4260 } else if (LineFullMatch (L, "\tjsr\ttosaddax")) {
4261 /* We know about this function */
4264 } else if (LineFullMatch (L, "\tjsr\ttosicmp")) {
4265 /* We know about this function */
4268 } else if (LineFullMatchX (L, Compares) >= 0) {
4271 } else if (LineFullMatchX (L, MakeBool) >= 0) {
4274 } else if (LineMatch (L, "\tjsr\t")) {
4275 /* Subroutine call, forget all register information */
4277 } else if (LineMatch (L, "\tlda\t")) {
4278 if (!RegAUsed (L) && !IsCondJump (NextInstruction (L))) {
4279 /* The value loaded is not used later, remove it */
4281 } else if (LineMatch (L, "\tlda\t(")) {
4282 if (IsXAddrMode (L)) {
4284 /* If X is zero and we have a 65C02 cpu, replace it by
4287 if (X == 0 && CPU == CPU_65C02) {
4288 unsigned Len = strlen (L->Line);
4289 L->Line [Len-3] = ')';
4290 L->Line [Len-2] = '\0';
4291 /* If Y and X are both zero, replace by load indirect
4292 * y and save one cycle in some cases.
4294 } else if (X == 0 && Y == 0) {
4296 const char* S = L->Line + 6;
4298 strcpy (Buf, "\tlda\t(");
4306 L = ReplaceLine (L, Buf);
4308 } else if (IsYAddrMode (L)) {
4309 /* lda (zp),y. If Y is zero and we have a 65C02 CPU,
4310 * replace it by an indirect load.
4312 if (Y == 0 && CPU == CPU_65C02) {
4313 unsigned Len = strlen (L->Line);
4314 L->Line [Len-3] = ')';
4315 L->Line [Len-2] = '\0';
4318 /* In any case invalidate A */
4320 } else if (LineMatch (L, "\tlda\t#$")) {
4321 /* Immidiate load into A */
4322 NewVal = GetHexNum (L->Line + 7);
4324 /* Load has no effect */
4326 } else if (NewVal == X) {
4327 /* Requested value is already in X */
4328 L = ReplaceLine (L, "\ttxa");
4329 } else if (NewVal == Y) {
4330 /* Requested value is already in Y */
4331 L = ReplaceLine (L, "\ttya");
4332 } else if (CPU == CPU_65C02 && A != -1) {
4333 /* Try ina/dea operators of 65C02 */
4334 if (NewVal == ((A - 1) & 0xFF)) {
4335 L = ReplaceLine (L, "\tdea");
4336 } else if (NewVal == ((A + 1) & 0xFF)) {
4337 L = ReplaceLine (L, "\tina");
4340 /* Anyway, the new value is now in A */
4343 /* Memory load into A */
4346 } else if (LineMatch (L, "\tldax\t")) {
4347 /* Memory load into A and X */
4349 } else if (LineMatch (L, "\tldx\t")) {
4350 if (!RegXUsed (L) && !IsCondJump (NextInstruction (L))) {
4351 /* The value loaded is not used later, remove it */
4353 } else if (LineMatch (L, "\tldx\t#$")) {
4354 /* Immidiate load into X */
4355 NewVal = GetHexNum (L->Line + 7);
4357 /* Load has no effect */
4359 } else if (NewVal == A) {
4360 /* Requested value is already in A */
4361 L = ReplaceLine (L, "\ttax");
4362 } else if (X != -1 && NewVal == ((X + 1) & 0xFF)) {
4363 /* Requested value is one more than current contents */
4364 L = ReplaceLine (L, "\tinx");
4365 } else if (X != -1 && NewVal == ((X - 1) & 0xFF)) {
4366 /* Requested value is one less than current contents */
4367 L = ReplaceLine (L, "\tdex");
4369 /* Anyway, the new value is now in X */
4372 /* Memory load into X */
4375 } else if (LineMatch (L, "\tldy\t")) {
4376 if (!RegYUsed (L) && !IsCondJump (NextInstruction (L))) {
4377 /* The value loaded is not used later, remove it */
4379 } else if (LineMatch (L, "\tldy\t#$")) {
4380 /* Immidiate load into Y */
4381 NewVal = GetHexNum (L->Line + 7);
4383 /* Load has no effect */
4385 } else if (NewVal == A) {
4386 /* Requested value is already in A */
4387 L = ReplaceLine (L, "\ttay");
4388 } else if (Y != -1 && NewVal == ((Y + 1) & 0xFF)) {
4389 /* Requested value is one more than current contents */
4390 L = ReplaceLine (L, "\tiny");
4391 } else if (Y != -1 && NewVal == ((Y - 1) & 0xFF)) {
4392 /* Requested value is one less than current contents */
4393 L = ReplaceLine (L, "\tdey");
4395 /* Anyway, the new value is now in Y */
4398 /* Memory load into Y */
4401 } else if (LineFullMatch (L, "\tlsr\ta")) {
4405 } else if (LineMatch (L, "\tora\t#$")) {
4407 A |= GetHexNum (L->Line + 7);
4409 } else if (LineMatch (L, "\tora\t")) {
4411 } else if (LineFullMatch (L, "\tpla")) {
4413 } else if (LineFullMatch (L, "\trol\ta")) {
4415 } else if (LineFullMatch (L, "\tror\ta")) {
4417 } else if (LineFullMatch (L, "\trts")) {
4419 } else if (LineFullMatch (L, "\trti")) {
4421 } else if (LineMatch (L, "\tsbc\t")) {
4422 if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
4423 L->Line[strlen(L->Line)-2] = '\0';
4426 } else if (CPU == CPU_65C02 && LineMatch (L, "\tst")) {
4427 /* Try to replace by stz if possible */
4428 if (A == 0 && LineMatch (L, "\tsta\t")) {
4429 /* Not indirect and not Y allowed */
4430 if (L->Line[5] != '(' && !IsYAddrMode (L)) {
4433 } else if (X == 0 && LineMatch (L, "\tstx\t")) {
4434 /* absolute,y not allowed */
4435 if (!IsYAddrMode (L)) {
4438 } else if (Y == 0 && LineMatch (L, "\tsty\t")) {
4439 /* sty and stz share all addressing modes */
4442 } else if (LineFullMatch (L, "\ttax")) {
4443 if (A != -1 && X == A) {
4444 /* Load has no effect */
4449 } else if (LineFullMatch (L, "\ttay")) {
4450 if (A != -1 && Y == A) {
4451 /* Load has no effect */
4456 } else if (LineFullMatch (L, "\ttsx")) {
4458 } else if (LineFullMatch (L, "\ttxa")) {
4459 if (X != -1 && A == X) {
4460 /* Load has no effect */
4465 } else if (LineFullMatch (L, "\ttya")) {
4466 if (Y != -1 && A == Y) {
4467 /* Load has no effect */
4474 /* Set to next line, handle deletions */
4475 L2 = NextCodeSegLine (L);
4483 /* Skip the label */
4484 L = NextCodeSegLine (L);
4491 static void OptBlocks (void)
4492 /* Optimize the register contents inside basic blocks */
4494 Line* L = FirstCode;
4496 L = OptOneBlock (L);
4502 static void OptJumps (void)
4503 /* Optimize jumps */
4505 static const char* Jumps [] = {
4515 Line* L = FirstCode;
4517 int I = LineMatchX (L, Jumps);
4519 Line* Target = GetTargetLine (L->Line+5);
4520 if (Target->Index > L->Index) {
4521 /* This is a forward jump. Backward jumps are handled
4522 * automagically by the assembler.
4524 unsigned Distance = GetJumpDistance (L, Target);
4525 if (Distance < 123) { /* Safety */
4526 L->Line [1] = 'b'; /* Make a short branch */
4527 L->Size = 2; /* Set new size */
4531 L = NextCodeLine (L);
4534 /* Special treatment for jumps on the 65C02 */
4535 if (CPU == CPU_65C02) {
4537 Line* L = FirstCode;
4539 if (LineMatch (L, "\tjmp\tL")) {
4540 Line* Target = GetTargetLine (L->Line+5);
4541 unsigned Distance = GetJumpDistance (L, Target);
4542 if (Distance < 123) { /* Safety */
4543 L->Line [1] = 'b'; /* Make a short branch */
4546 L->Size = 2; /* Set new size */
4549 L = NextCodeLine (L);
4557 static void OptRTS (void)
4558 /* Change sequences of jsr XXX/rts to jmp XXX */
4560 Line* L = FirstCode;
4562 if (LineMatch (L, "\tjsr\t")) {
4563 /* This is a jsr, get the next instruction */
4564 Line* L2 = NextCodeLine (L);
4565 if (L2 && LineFullMatch (L2, "\trts")) {
4566 /* We found a sequence */
4572 /* Try the next line */
4573 L = NextCodeLine (L);
4579 /*****************************************************************************/
4581 /*****************************************************************************/
4585 void OptDoOpt (void)
4586 /* Run the optimizer over the collected stuff */
4588 typedef void (*OptFunc)(void);
4590 /* Table with optimizer steps - are called in this order */
4591 static const OptFunc OptFuncs [] = {
4592 OptCompares1, /* Optimize compares - first step */
4593 OptDeadJumps, /* Remove dead jumps */
4594 OptLoads, /* Remove unnecessary loads */
4595 OptRegLoads, /* Remove unnecessary register loads */
4596 OptPtrOps, /* Optimize stores through pointers */
4597 OptRegVars, /* Optimize use of register variables */
4598 OptDoubleJumps, /* Remove jump cascades - must be used before OptNeg */
4599 OptNeg, /* Remove unnecessary boolean negates */
4600 OptJumpRTS, /* Replace jumps to an RTS by an RTS */
4601 OptBoolTransforms, /* Optimize boolean transforms */
4602 OptCompares2, /* Optimize compares */
4603 OptTests, /* Remove unnecessary tests */
4604 OptBitOps, /* Optimize bit operations */
4605 OptTriples, /* Optimize several triples */
4606 OptBlocks, /* Optimize basic blocks */
4607 OptRegLoads, /* Remove unnecessary register loads (another pass) */
4608 OptBlocks, /* Optimize basic blocks */
4609 OptJumps, /* Optimize jumps */
4610 OptRTS, /* Optimize jsr/rts sequences */
4613 unsigned long Flags;
4616 /* Find and remember the first line of code */
4619 /* Mark all lines inside the code segment */
4622 /* Create a list of all local labels for fast access */
4625 /* Ok, now start the real optimizations */
4627 for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I, Flags <<= 1) {
4628 if ((OptDisable & Flags) == 0) {
4630 } else if (Verbosity > 0 || Debug) {
4631 printf ("Optimizer pass %u skipped\n", I);