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 /*****************************************************************************/
40 #include "../common/xmalloc.h"
53 /*****************************************************************************/
55 /*****************************************************************************/
59 /* Bitset of flags that switch the different optimizer passes */
60 unsigned long OptDisable = 0;
64 /* Bitmapped flags for the Flags field in the Line struct */
65 #define OF_CODE 0x0001 /* This line is in a code segment */
67 /* Pointer to first code line */
68 static Line* FirstCode;
71 static Line** Labels = 0; /* Pointers to label lines */
72 static unsigned LabelCount = 0; /* Count of local labels found */
74 /* A collection of lines */
75 typedef struct LineColl_ LineColl;
77 unsigned Count; /* Count of lines in the collection */
78 unsigned Max; /* Maximum count of lines */
79 Line* Lines[1]; /* Lines, dynamically allocated */
84 /* Calculate the element count of a table */
85 #define COUNT(T) (sizeof (T) / sizeof (T [0]))
87 /* Macro to increment and decrement register contents if they're valid */
88 #define INC(reg,val) if ((reg) >= 0) (reg) = ((reg) + val) & 0xFF
89 #define DEC(reg,val) if ((reg) >= 0) (reg) = ((reg) - val) & 0xFF
91 /* Defines for the conditions in a compare */
103 /* Defines for registers */
104 #define REG_NONE 0x00
108 #define REG_AX (REG_A | REG_X)
109 #define REG_ALL (REG_A | REG_X | REG_Y)
111 /* Description of the commands */
112 static const struct {
113 const char* Insn; /* Instruction */
114 unsigned char FullMatch; /* Match full instuction? */
115 unsigned char Use; /* Registers used */
116 unsigned char Load; /* Registers loaded */
118 { "\tadc\t", 0, REG_A, REG_NONE },
119 { "\tand\t", 0, REG_A, REG_NONE },
120 { "\tasl\ta", 1, REG_A, REG_NONE },
121 { "\tasl\t", 0, REG_NONE, REG_NONE },
122 { "\tclc", 1, REG_NONE, REG_NONE },
123 { "\tcld", 1, REG_NONE, REG_NONE },
124 { "\tcli", 1, REG_NONE, REG_NONE },
125 { "\tcmp\t", 0, REG_A, REG_NONE },
126 { "\tcpx\t", 0, REG_X, REG_NONE },
127 { "\tcpy\t", 0, REG_Y, REG_NONE },
128 { "\tdea", 1, REG_A, REG_NONE },
129 { "\tdec\ta", 1, REG_A, REG_NONE },
130 { "\tdec\t", 0, REG_NONE, REG_NONE },
131 { "\tdex", 1, REG_X, REG_NONE },
132 { "\tdey", 1, REG_Y, REG_NONE },
133 { "\teor\t", 0, REG_A, REG_NONE },
134 { "\tina", 1, REG_A, REG_NONE },
135 { "\tinc\ta", 1, REG_A, REG_NONE },
136 { "\tinc\t", 0, REG_NONE, REG_NONE },
137 { "\tinx", 1, REG_X, REG_NONE },
138 { "\tiny", 1, REG_Y, REG_NONE },
139 { "\tjsr\tbool", 0, REG_NONE, REG_AX },
140 { "\tjsr\tdecaxy", 1, REG_ALL, REG_AX },
141 { "\tjsr\tdecax", 0, REG_AX, REG_AX },
142 { "\tjsr\tldax0sp", 1, REG_Y, REG_AX },
143 { "\tjsr\tldaxysp", 1, REG_Y, REG_AX },
144 { "\tjsr\tpusha", 1, REG_A, REG_Y },
145 { "\tjsr\tpusha0", 1, REG_A, REG_X | REG_Y },
146 { "\tjsr\tpushax", 1, REG_AX, REG_Y },
147 { "\tjsr\tpushw0sp", 1, REG_NONE, REG_ALL },
148 { "\tjsr\tpushwysp", 1, REG_Y, REG_ALL },
149 { "\tjsr\ttosicmp", 1, REG_AX, REG_ALL },
150 { "\tlda\t", 0, REG_NONE, REG_A },
151 { "\tldax\t", 0, REG_NONE, REG_AX },
152 { "\tldx\t", 0, REG_NONE, REG_X },
153 { "\tldy\t", 0, REG_NONE, REG_Y },
154 { "\tlsr\ta", 1, REG_A, REG_NONE },
155 { "\tlsr\t", 0, REG_NONE, REG_NONE },
156 { "\tnop", 1, REG_NONE, REG_NONE },
157 { "\tora\t", 0, REG_A, REG_NONE },
158 { "\tpha", 1, REG_A, REG_NONE },
159 { "\tphp", 1, REG_NONE, REG_NONE },
160 { "\tpla", 1, REG_NONE, REG_A },
161 { "\tplp", 1, REG_NONE, REG_NONE },
162 { "\trol\ta", 1, REG_A, REG_A },
163 { "\trol\t", 0, REG_NONE, REG_NONE },
164 { "\tror\ta", 1, REG_A, REG_A },
165 { "\tror\t", 0, REG_NONE, REG_NONE },
166 { "\tsbc\t", 0, REG_A, REG_NONE },
167 { "\tsec", 1, REG_NONE, REG_NONE },
168 { "\tsed", 1, REG_NONE, REG_NONE },
169 { "\tsei", 1, REG_NONE, REG_NONE },
170 { "\tsta\t", 0, REG_A, REG_NONE },
171 { "\tstx\t", 0, REG_X, REG_NONE },
172 { "\tsty\t", 0, REG_Y, REG_NONE },
173 { "\tstz\t", 0, REG_NONE, REG_NONE },
174 { "\ttax", 1, REG_A, REG_X },
175 { "\ttay", 1, REG_A, REG_Y },
176 { "\ttsx", 1, REG_NONE, REG_X },
177 { "\ttxa", 1, REG_X, REG_A },
178 { "\ttya", 1, REG_Y, REG_A },
183 /* Table with the compare suffixes */
184 static const char CmpSuffixTab [][4] = {
185 "eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule"
188 /* Table used to invert a condition, indexed by condition */
189 static const unsigned char CmpInvertTab [] = {
191 CMP_LE, CMP_LT, CMP_GE, CMP_GT,
192 CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT
195 /* Table to show which compares are signed (use the N flag) */
196 static const char CmpSignedTab [] = {
197 0, 0, 1, 1, 1, 1, 0, 0, 0, 0
202 /* Lists of branches */
203 static const char* ShortBranches [] = {
214 static const char* LongBranches [] = {
228 /*****************************************************************************/
230 /*****************************************************************************/
234 static unsigned EstimateSize (Line* L);
235 /* Estimate the size of an instruction */
237 static int IsLocalLabel (const Line* L);
238 /* Return true if the line is a local label line */
240 static unsigned GetLabelNum (const char* L);
241 /* Return the label number of a label line */
243 static unsigned RVUInt1 (Line* L, LineColl* LC, unsigned Used, unsigned Unused);
244 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
248 /*****************************************************************************/
250 /*****************************************************************************/
254 static Line* NewLineAfter (Line* LineBefore, const char* Format, ...)
255 /* Create a new line, insert it after L and return it. The new line is marked
261 /* Format the new line and add it */
263 va_start (ap, Format);
264 L = NewCodeLineAfter (LineBefore, Format, ap);
267 /* Make the line a code line */
270 /* Estimate the code size */
271 L->Size = EstimateSize (L);
273 /* Return the new line */
279 static Line* NewLabelAfter (Line* L, unsigned Label)
280 /* Add a new line with a definition of a local label after the line L */
284 /* Create the label */
285 sprintf (Buf, "L%04X:", Label);
287 /* Create a new line */
288 L = NewLineAfter (L, Buf);
290 /* Insert this label into the label list */
293 /* Return the new line */
299 static void FreeLine (Line* L)
300 /* Remove a line from the list and free it */
302 /* If this is a label line, remove it from the label list */
303 if (IsLocalLabel (L)) {
304 Labels [GetLabelNum (L->Line)] = 0;
307 /* Unlink the line */
313 static Line* ReplaceLine (Line* L, const char* Format, ...)
314 /* Replace one line by another */
319 /* Format the new line */
321 va_start (ap, Format);
322 vsprintf (S, Format, ap);
325 /* Get the length of the new line */
328 /* We can copy the line if the old line has space enough */
331 /* Just copy the new line, but don't update the length */
332 memcpy (L->Line, S, Len);
333 L->Line [Len] = '\0';
337 /* We must allocate new space */
338 Line* NewLine = xmalloc (sizeof (Line) + Len);
340 /* Set the values in the new struct */
341 NewLine->Flags = L->Flags;
342 NewLine->Index = L->Index;
343 NewLine->Size = L->Size; /* Hmm ... */
345 memcpy (NewLine->Line, S, Len + 1);
347 /* Replace the old struct in the list */
348 NewLine->Next = L->Next;
350 NewLine->Next->Prev = NewLine;
355 NewLine->Prev = L->Prev;
357 NewLine->Prev->Next = NewLine;
363 /* Free the old struct */
368 /* Estimate the new size */
369 if (L->Flags & OF_CODE) {
370 L->Size = EstimateSize (L);
373 /* Return the line */
379 static Line* PrevCodeLine (Line* L)
380 /* Return the previous line containing code */
384 if (L->Flags & OF_CODE && L->Line [0] != '+') {
394 static Line* NextCodeSegLine (Line* L)
395 /* Return the next line in the code segment */
399 if (L->Flags & OF_CODE) {
409 static Line* NextCodeLine (Line* L)
410 /* Return the next line containing code */
414 if ((L->Flags & OF_CODE) != 0 && L->Line [0] != '+') {
424 static Line* NextInstruction (Line* L)
425 /* Return the next line containing code, ignoring labels. */
428 L = NextCodeLine (L);
429 } while (L && (L->Line[0] == '+' || IsLocalLabel(L)));
435 static void FreeLines (Line* Start, Line* End)
436 /* Delete all lines from Start to End, both inclusive */
441 Start = NextCodeSegLine (Start);
448 /*****************************************************************************/
449 /* Line Collections */
450 /*****************************************************************************/
454 static LineColl* NewLineColl (unsigned Size)
455 /* Create a new line collection and return it */
457 /* Allocate memory */
458 LineColl* LC = xmalloc (sizeof (LineColl) + sizeof (Line) * (Size-1));
460 /* Initialize members */
464 /* Return the new collection */
470 static void FreeLineColl (LineColl* LC)
471 /* Delete a line collection */
478 static int LCAddLine (LineColl* LC, Line* L)
479 /* Add a line. Return 0 if no space available, return 1 otherwise */
481 /* Check if there is enough space available */
482 if (LC->Count >= LC->Max) {
483 /* No room available */
488 LC->Lines [LC->Count++] = L;
496 static int LCHasLine (LineColl* LC, Line* L)
497 /* Check if the given line is in the collection */
500 for (I = 0; I < LC->Count; ++I) {
501 if (LC->Lines[I] == L) {
510 /*****************************************************************************/
511 /* Test a line for several things */
512 /*****************************************************************************/
516 static int IsLocalLabel (const Line* L)
517 /* Return true if the line is a local label line */
519 return (L->Line [0] == 'L' && isxdigit (L->Line [1]));
524 static int IsLabel (const Line* L)
525 /* Return true if the line is a label line */
527 return (L->Line [0] == 'L' && isxdigit (L->Line [1])) ||
528 (L->Line [0] == '_');;
533 static int IsHintLine (const Line* L)
534 /* Return true if the line contains an optimizer hint */
536 return L->Line [0] == '+';
541 static int IsSegHint (const Line* L)
542 /* Return true if the given line contains a segment hint */
544 return (L->Line [0] == '+' && strncmp (L->Line + 1, "seg:", 4) == 0);
549 static int IsHint (const Line* L, const char* Hint)
550 /* Check if the line contains a given hint */
552 return (L->Line [0] == '+' && strcmp (L->Line + 1, Hint) == 0);
557 static int IsCondJump (Line* L)
558 /* Return true if the line contains a conditional jump */
560 return (L->Line [0] == '\t' &&
561 (strncmp (L->Line + 1, "beq\t", 4) == 0 ||
562 strncmp (L->Line + 1, "bne\t", 4) == 0 ||
563 strncmp (L->Line + 1, "jeq\t", 4) == 0 ||
564 strncmp (L->Line + 1, "jne\t", 4) == 0));
569 static int IsXAddrMode (Line* L)
570 /* Return true if the given line does use the X register */
572 unsigned Len = strlen (L->Line);
573 return (strcmp (L->Line + Len - 3, ",x)") == 0 ||
574 strcmp (L->Line + Len - 2, ",x") == 0);
579 static int NoXAddrMode (Line* L)
580 /* Return true if the given line does use the X register */
582 return !IsXAddrMode (L);
587 static int IsYAddrMode (Line* L)
588 /* Return true if the given line does use the Y register */
590 unsigned Len = strlen (L->Line);
591 return (strcmp (L->Line + Len - 2, ",y") == 0);
596 static Line* FindHint (Line* L, const char* Hint)
597 /* Search for a line with the given hint */
600 if (IsHint (L, Hint)) {
610 static unsigned GetHexNum (const char* S)
611 /* Get a hex number from a string */
615 while (isxdigit (S [I])) {
616 int C = (unsigned char) (S [I++]);
622 Val = (Val << 4) + C;
629 static unsigned GetLabelNum (const char* L)
630 /* Return the label number of a label line */
633 return GetHexNum (L+1);
638 static Line* GetTargetLine (const char* L)
639 /* Get the line with the target label of a jump. L must be a pointer to the
640 * string containing the label number.
645 /* Get the label number of the target */
646 unsigned Label = GetLabelNum (L);
647 CHECK (Label < LabelCount);
649 /* Get the line with this label */
650 Target = Labels [Label];
651 CHECK (Target != 0 && (Target->Flags & OF_CODE) != 0);
659 static unsigned GetJumpDistance (Line* L, Line* Target)
660 /* Get the distance between both lines */
662 unsigned Distance = 0;
665 if (Target->Index > L->Index) {
666 /* This is a forward jump. */
668 L = NextCodeLine (L);
670 } while (L != Target);
672 /* This is a backward jump */
674 L = PrevCodeLine (L);
676 } while (L != Target);
680 /* Return the calculated distance */
686 static int LineMatch (const Line* L, const char* Start)
687 /* Check if the start of the line matches Start */
689 return strncmp (L->Line, Start, strlen (Start)) == 0;
694 static int LineFullMatch (const Line* L, const char* Start)
695 /* Check if the matches Start */
697 return strcmp (L->Line, Start) == 0;
702 static int LineMatchX (const Line* L, const char** Start)
703 /* Check the start of the line against a list of patterns. Return the
704 * number of the pattern that matched, or -1 in case of no match.
709 if (LineMatch (L, *Start)) {
722 static int LineFullMatchX (const Line* L, const char** Start)
723 /* Check the the line against a list of patterns. Return the
724 * number of the pattern that matched, or -1 in case of no match.
729 if (LineFullMatch (L, *Start)) {
742 static int IsLoadAX (Line* L1, Line* L2)
743 /* Check if the both lines load a static variable into ax. That is, both lines
749 return LineMatch (L1, "\tlda\t") &&
750 LineMatch (L2, "\tldx\t") &&
751 strncmp (L1->Line+5, L2->Line+5, strlen (L1->Line+5)) == 0 &&
752 strcmp (L2->Line+strlen(L1->Line), "+1") == 0;
757 /*****************************************************************************/
758 /* Initial optimizer setup */
759 /*****************************************************************************/
763 static void FindCodeStart (void)
764 /* Find and remember the first line of actual code */
766 Line* L = FindHint (FirstLine, "end_of_preamble");
767 FirstCode = L? L->Next : 0;
772 static unsigned EstimateDataSize (Line* L, unsigned Chunk)
773 /* Estimate the size of a .byte, .word or .dword command */
775 unsigned Size = Chunk;
777 while ((S = strchr (S, ',')) != 0) {
786 static unsigned EstimateSize (Line* L)
787 /* Estimate the size of an instruction */
789 static const char* OneByteCmds [] = {
805 if (L->Line [0] != '\t') {
808 if (LineMatch (L, "\tldax\t")) {
809 /* Immidiate load of both, A and X */
812 if (LineMatch (L, "\tld")) {
813 OpStart = L->Line [5];
814 return (OpStart == '#' || OpStart == '(')? 2 : 3;
816 if (LineMatch (L, "\tst")) {
817 OpStart = L->Line [5];
818 return (OpStart == '(')? 2 : 3;
820 if (LineMatch (L, "\t.byte\t")) {
821 return EstimateDataSize (L, 1);
823 if (LineMatch (L, "\t.word\t")) {
824 return EstimateDataSize (L, 2);
826 if (LineMatch (L, "\t.dword\t")) {
827 return EstimateDataSize (L, 4);
829 if (LineMatchX (L, ShortBranches) >= 0) {
832 if (LineMatchX (L, LongBranches) >= 0) {
835 if (LineMatchX (L, OneByteCmds) >= 0) {
843 static void MarkCodeLines (void)
844 /* Mark all lines that are inside a code segment */
850 InCode = IsHint (L, "seg:code");
851 } else if (InCode && L->Line[0] != '\0') {
853 L->Size = EstimateSize (L);
861 static void CreateLabelList (void)
862 /* Create a list with pointers to local labels */
868 /* Get the next label number. This is also the current label count.
869 * Make some room for more labels when optimizing code.
871 LabelCount = GetLabel () + 100;
873 /* Allocate memory for the array and clear it */
874 Labels = xmalloc (LabelCount * sizeof (Line*));
875 for (I = 0; I < LabelCount; ++I) {
879 /* Walk through the code and insert all label lines */
882 if (IsLocalLabel (L)) {
883 unsigned LabelNum = GetLabelNum (L->Line);
884 CHECK (LabelNum < LabelCount);
885 Labels [LabelNum] = L;
893 static unsigned AllocLabel (void)
894 /* Get a new label. The current code does not realloc the label list, so there
895 * must be room enough in the current list.
900 /* Search for a free slot, start at 1, since 0 is "no label" */
901 for (I = 1; I < LabelCount; ++I) {
902 if (Labels[I] == 0) {
903 /* Found a free slot */
908 /* No label space available */
909 Internal ("Out of label space in the optimizer");
917 /*****************************************************************************/
918 /* Helper functions */
919 /*****************************************************************************/
923 static int GetNextCodeLines (Line* L, Line** Lines, unsigned Count)
924 /* Get a number of code lines ignoring hints and other stuff. The function
925 * returns 1 if we got the lines and 0 if we are at the end of the code
926 * segment or if we hit a label.
931 /* Get the next valid line */
933 L = NextCodeLine (L);
934 } while (L && IsHintLine (L));
936 /* Did we get one? */
937 if (L == 0 || IsLabel (L)) {
942 /* Remember the line */
952 static int FindCond (const char* Suffix)
953 /* Map a condition suffix to a code. Return the code or -1 on failure */
958 for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) {
959 if (strncmp (Suffix, CmpSuffixTab [I], strlen (CmpSuffixTab[I])) == 0) {
971 static int CheckAndGetIntCmp (const Line* JSR, const Line* JMP)
972 /* Helper function to check for a compare subroutine call followed by a
973 * conditional branch. Will return the condition found, or -1 if no
974 * or invalid condition.
981 /* Extract the condition from the function name. */
982 if ((Cond [0] = JSR->Line [8]) == 'u') {
983 Cond [1] = JSR->Line [9];
984 Cond [2] = JSR->Line [10];
986 Tail = JSR->Line + 11;
988 Cond [1] = JSR->Line [9];
990 Tail = JSR->Line + 10;
993 /* Check if this is indeed an integer function */
994 if (strcmp (Tail, "ax") != 0) {
999 /* Get the condition code */
1000 C = FindCond (Cond);
1006 /* Invert the code if we jump on condition not met. */
1007 if (JMP->Line [2] == 'e' && JMP->Line [3] == 'q') {
1008 /* Jumps if condition false, invert condition */
1009 C = CmpInvertTab [C];
1012 /* Return the condition code */
1018 static int TosCmpFunc (Line* L)
1019 /* Check if this is a call to one of the TOS compare functions (tosgtax).
1020 * Return the condition code or -1 if not found.
1023 if (LineMatch (L, "\tjsr\ttos") &&
1024 strcmp (L->Line+strlen(L->Line)-2, "ax") == 0) {
1026 /* Ok, found. Get the condition. */
1027 return FindCond (L->Line+8);
1038 static int IsUnsignedCmp (int Code)
1039 /* Check if this is an unsigned compare */
1042 return CmpSignedTab [Code] == 0;
1047 static void InvertZJump (Line* L)
1048 /* Invert a jeq/jne jump */
1050 if (L->Line [2] == 'n' && L->Line [3] == 'e') {
1051 /* This was a bne/jne */
1055 /* This was (hopefully) a beq/jeq */
1063 static int FindCmd (Line* L)
1067 /* Search for the known patterns */
1068 for (I = 0; I < COUNT(CmdDesc); ++I) {
1069 if (CmdDesc[I].FullMatch) {
1070 if (LineFullMatch (L, CmdDesc[I].Insn)) {
1075 if (LineMatch (L, CmdDesc[I].Insn)) {
1087 static unsigned RVUInt2 (Line* L,
1088 LineColl* LC, /* To remember visited lines */
1089 unsigned Used, /* Definitely used registers */
1090 unsigned Unused) /* Definitely unused registers */
1091 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
1095 /* Check the following instructions. We classifiy them into primary
1096 * loads (register value not used), neutral (check next instruction),
1097 * and unknown (assume register was used).
1103 /* Get the next line and follow jumps */
1106 /* Handle jumps to local labels (continue there) */
1107 if (LineMatch (L, "\tjmp\tL") || LineMatch (L, "\tbra\tL")) {
1108 /* Get the target of the jump */
1109 L = GetTargetLine (L->Line+5);
1112 /* Get the next instruction line */
1113 L = NextInstruction (L);
1115 /* Bail out if we're done */
1116 if (L == 0 || IsLabel (L)) {
1117 /* Something is wrong */
1121 /* Check if we had this line already. If so, bail out, if not,
1122 * add it to the list of known lines.
1124 if (LCHasLine (LC, L) || !LCAddLine (LC, L)) {
1128 } while (LineMatch (L, "\tjmp\tL") || LineMatch (L, "\tbra\tL"));
1130 /* Special handling for branches */
1131 if (LineMatchX (L, ShortBranches) >= 0 ||
1132 LineMatchX (L, LongBranches) >= 0) {
1133 const char* Target = L->Line+5;
1134 if (Target[0] == 'L') {
1135 /* Jump to local label. Check the register usage starting at
1136 * the branch target and at the code following the branch.
1137 * All registers that are unused in both execution flows are
1138 * returned as unused.
1141 U2 = RVUInt1 (GetTargetLine (Target), LC, Used, Unused);
1142 U1 = RVUInt1 (L, LC, Used, Unused);
1143 return U1 | U2; /* Used in any of the branches */
1147 /* Search for the instruction in this line */
1150 /* If we don't find it, assume all other registers are */
1155 /* Evaluate the use flags, check for addressing modes */
1157 if (IsXAddrMode (L)) {
1159 } else if (IsYAddrMode (L)) {
1163 /* Remove registers that were already new loaded */
1166 /* Remember the remaining registers */
1170 /* Evaluate the load flags */
1171 R = CmdDesc[I].Load;
1173 /* Remove registers that were already used */
1176 /* Remember the remaining registers */
1180 /* If we know about all registers, bail out */
1181 if ((Used | Unused) == REG_ALL) {
1187 /* Return to the caller the complement of all unused registers */
1188 return ~Unused & REG_ALL;
1193 static unsigned RVUInt1 (Line* L,
1194 LineColl* LC, /* To remember visited lines */
1195 unsigned Used, /* Definitely used registers */
1196 unsigned Unused) /* Definitely unused registers */
1197 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
1199 /* Remember the current count of the line collection */
1200 unsigned Count = LC->Count;
1202 /* Call the worker routine */
1203 unsigned R = RVUInt2 (L, LC, Used, Unused);
1205 /* Restore the old count */
1208 /* Return the result */
1214 static unsigned RegValUsed (Line* Start)
1215 /* Check the next instructions after the one in L for register usage. If
1216 * a register is used as an index, or in a store or other instruction, it
1217 * is assumed to be used. If a register is loaded with a value, before it
1218 * was used by one of the actions described above, it is assumed unused.
1219 * If the end of the lookahead is reached, all registers that are uncertain
1220 * are marked as used.
1221 * The result of the search is returned.
1226 /* Create a new line collection and enter the start line */
1227 LineColl* LC = NewLineColl (256);
1228 LCAddLine (LC, Start);
1230 /* Call the recursive subfunction */
1231 R = RVUInt1 (Start, LC, REG_NONE, REG_NONE);
1233 /* Delete the line collection */
1236 /* Return the registers used */
1242 static int RegAUsed (Line* Start)
1243 /* Check if the value in A is used. */
1245 return (RegValUsed (Start) & REG_A) != 0;
1250 static int RegXUsed (Line* Start)
1251 /* Check if the value in X is used. */
1253 return (RegValUsed (Start) & REG_X) != 0;
1258 static int RegYUsed (Line* Start)
1259 /* Check if the value in Y is used. */
1261 return (RegValUsed (Start) & REG_Y) != 0;
1266 /*****************************************************************************/
1267 /* Real optimizer routines */
1268 /*****************************************************************************/
1272 static void OptCompares1 (void)
1273 /* Try to optimize the integer compare subroutines. */
1275 Line* L2[10]; /* Line lookahead */
1276 int Cond; /* Condition to evaluate */
1277 unsigned Label; /* Local label number */
1278 unsigned Offs; /* Stack offset */
1279 Line* DelStart; /* First line to delete */
1281 Line* L = FirstCode;
1284 /* Search for compares of local byte sized variables. This looks
1296 * Replace it by a direct compare:
1304 if (LineMatch (L, "\tldy\t#$") &&
1305 GetNextCodeLines (L, L2, 7) &&
1306 LineFullMatch (L2[0], "\tldx\t#$00") &&
1307 LineFullMatch (L2[1], "\tlda\t(sp),y") &&
1308 LineFullMatch (L2[2], "\tjsr\tpushax") &&
1309 LineMatch (L2[3], "\tldy\t#$") &&
1310 LineFullMatch (L2[4], "\tldx\t#$00") &&
1311 LineFullMatch (L2[5], "\tlda\t(sp),y") &&
1312 (Cond = TosCmpFunc (L2[6])) >= 0) {
1314 /* Get the stack offset and correct it, since we will remove
1317 Offs = GetHexNum (L2[3]->Line+7) - 2;
1320 L = NewLineAfter (L, "\tlda\t(sp),y");
1321 L = NewLineAfter (L, "\tldy\t#$%02X", Offs);
1322 L = NewLineAfter (L, "\tcmp\t(sp),y");
1323 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1325 /* Remove the old cruft */
1326 FreeLines (L2[0], L2[6]);
1329 /* Compares of byte sized global variables */
1330 else if (LineFullMatch (L, "\tldx\t#$00") &&
1331 GetNextCodeLines (L, L2, 5) &&
1332 LineMatch (L2[0], "\tlda\t") &&
1333 LineFullMatch (L2[1], "\tjsr\tpushax") &&
1334 LineFullMatch (L2[2], "\tldx\t#$00") &&
1335 LineMatch (L2[3], "\tlda\t") &&
1336 (Cond = TosCmpFunc (L2[4])) >= 0) {
1339 if (IsXAddrMode (L2[0])) {
1340 /* The load is X indirect, so we may not remove the load
1341 * of the X register.
1346 L = ReplaceLine (L, L2[0]->Line);
1349 L = NewLineAfter (L, "\tcmp\t%s", L2[3]->Line+5);
1350 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1352 /* Remove the old cruft */
1353 FreeLines (DelStart, L2[4]);
1357 /* Byte sized local to global */
1358 else if (LineMatch (L, "\tldy\t#$") &&
1359 GetNextCodeLines (L, L2, 6) &&
1360 LineFullMatch (L2[0], "\tldx\t#$00") &&
1361 LineFullMatch (L2[1], "\tlda\t(sp),y") &&
1362 LineFullMatch (L2[2], "\tjsr\tpushax") &&
1363 LineFullMatch (L2[3], "\tldx\t#$00") &&
1364 LineMatch (L2[4], "\tlda\t") &&
1365 (Cond = TosCmpFunc (L2[5])) >= 0) {
1368 L = NewLineAfter (L, L2[1]->Line);
1369 L = NewLineAfter (L, "\tcmp\t%s", L2[4]->Line+5);
1370 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1372 /* Remove the old cruft */
1373 FreeLines (L2[0], L2[5]);
1377 /* Byte sized global to local */
1378 else if (LineFullMatch (L, "\tldx\t#$00") &&
1379 GetNextCodeLines (L, L2, 6) &&
1380 LineMatch (L2[0], "\tlda\t") &&
1381 LineFullMatch (L2[1], "\tjsr\tpushax") &&
1382 LineMatch (L2[2], "\tldy\t#$") &&
1383 LineFullMatch (L2[3], "\tldx\t#$00") &&
1384 LineFullMatch (L2[4], "\tlda\t(sp),y") &&
1385 (Cond = TosCmpFunc (L2[5])) >= 0) {
1387 /* Get the stack offset and correct it, since we will remove
1390 Offs = GetHexNum (L2[2]->Line+7) - 2;
1393 if (IsXAddrMode (L2[0])) {
1394 /* The load is X indirect, so we may not remove the load
1395 * of the X register.
1400 L = ReplaceLine (L, L2[0]->Line);
1403 L = NewLineAfter (L, "\tldy\t#$%02X", Offs);
1404 L = NewLineAfter (L, "\tcmp\t(sp),y");
1405 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1407 /* Remove the old cruft */
1408 FreeLines (DelStart, L2[5]);
1412 /* Search for unsigned compares against global variables. This looks
1420 * Replace that by a direct compare:
1428 else if (LineFullMatch (L, "\tjsr\tpushax") &&
1429 GetNextCodeLines (L, L2, 3) &&
1430 IsLoadAX (L2[0], L2[1]) &&
1431 (Cond = TosCmpFunc (L2[2])) >= 0 &&
1432 IsUnsignedCmp (Cond)) {
1434 /* Get a free label number */
1435 Label = AllocLabel ();
1437 /* Replace the code */
1438 L = ReplaceLine (L, "\tcpx\t%s", L2[1]->Line+5);
1439 L = NewLineAfter (L, "\tbne\tL%04X", Label);
1440 L = NewLineAfter (L, "\tcmp\t%s", L2[0]->Line+5);
1441 L = NewLabelAfter(L, Label);
1442 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1444 /* Remove the old code */
1445 FreeLines (L2[0], L2[2]);
1449 L = NextCodeLine (L);
1455 static void OptDeadJumps (void)
1456 /* Remove jumps to the following instruction */
1458 static const char* Jumps [] = {
1467 Line* L = FirstCode;
1470 /* Get a pointer to the next instruction line */
1471 Line* NextLine = NextInstruction (L);
1473 /* Is this line a jump? */
1474 int I = LineMatchX (L, Jumps);
1476 /* Yes. Get the target label, skip labels */
1477 Line* Target = NextInstruction (GetTargetLine (L->Line+5));
1479 /* If the target label is the next line, remove the jump */
1480 if (Target == NextLine) {
1485 /* Go to the next line */
1492 static void OptLoads (void)
1493 /* Remove unnecessary loads of values */
1497 Line* L = FirstCode;
1518 * This change will cost 3 cycles (one additional jump inside the
1519 * subroutine), but it saves a lot of code (6 bytes per occurrence),
1520 * so we will accept the overhead. It may even be possible to rewrite
1521 * the library routine to get rid of the additional overhead.
1523 if (LineMatch (L, "\tldy\t#$") &&
1524 GetNextCodeLines (L, L2, 5) &&
1525 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
1526 LineFullMatch (L2 [1], "\ttax") &&
1527 LineFullMatch (L2 [2], "\tdey") &&
1528 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
1529 LineFullMatch (L2 [4], "\tjsr\tpushax")) {
1531 /* Found - replace it */
1532 if (LineFullMatch (L, "\tldy\t#$01")) {
1533 /* Word at offset zero */
1535 L = ReplaceLine (L2 [4], "\tjsr\tpushw0sp");
1537 ReplaceLine (L2 [4], "\tjsr\tpushwysp");
1540 /* Delete the remaining lines */
1541 FreeLines (L2 [0], L2 [3]);
1564 * This change will cost 2 cycles, but it saves a lot of code (6 bytes
1565 * per occurrence), so we will accept the overhead. It may even be
1566 * possible to rewrite the library routine to get rid of the additional
1569 if (LineMatch (L, "\tldy\t#$") &&
1570 GetNextCodeLines (L, L2, 6) &&
1571 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
1572 LineFullMatch (L2 [1], "\ttax") &&
1573 LineFullMatch (L2 [2], "\tdey") &&
1574 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
1575 LineMatch (L2 [4], "\tldy\t#$") &&
1576 LineFullMatch (L2 [5], "\tjsr\tldauidx")) {
1578 /* Found - replace it */
1579 L2 [4]->Line [3] = 'x'; /* Change to ldx */
1580 if (LineFullMatch (L, "\tldy\t#$01")) {
1581 /* Word at offset zero */
1583 L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp");
1585 ReplaceLine (L2 [5], "\tjsr\tldauiysp");
1588 /* Delete the remaining lines */
1589 FreeLines (L2 [0], L2 [3]);
1601 if (LineFullMatch (L, "\tlda\t(sp),y") &&
1602 GetNextCodeLines (L, L2, 1) &&
1603 LineFullMatch (L2 [0], "\tjsr\tpusha")) {
1605 /* Found, replace it */
1606 L = ReplaceLine (L, "\tjsr\tpushaysp");
1610 /* All other patterns start with this one: */
1611 if (!LineFullMatch (L, "\tldx\t#$00")) {
1621 * and replace it by:
1626 if (GetNextCodeLines (L, L2, 1) &&
1627 LineFullMatch (L2 [0], "\tjsr\tpushax")) {
1629 /* Replace the subroutine call */
1630 L = ReplaceLine (L, "\tjsr\tpusha0");
1632 /* Remove the unnecessary line */
1642 * and replace it by:
1648 else if (GetNextCodeLines (L, L2, 2) &&
1649 LineMatch (L2 [0], "\tlda\t") &&
1650 LineFullMatch (L2 [1], "\tjsr\tpushax")) {
1652 /* Be sure, X is not used in the load */
1653 if (NoXAddrMode (L2 [0])) {
1655 /* Replace the subroutine call */
1656 L2 [1] = ReplaceLine (L2 [1], "\tjsr\tpusha0");
1658 /* Remove the unnecessary load */
1661 /* L must be valid */
1673 * and replace it by:
1678 else if (GetNextCodeLines (L, L2, 2) &&
1679 LineMatch (L2 [0], "\tlda\t") &&
1680 LineMatch (L2 [1], "\tcmp\t#$")) {
1682 /* Be sure, X is not used in the load */
1683 if (NoXAddrMode (L2 [0])) {
1685 /* Remove the unnecessary load */
1688 /* L must be valid */
1699 * and replace it by:
1704 else if (GetNextCodeLines (L, L2, 2) &&
1705 LineMatch (L2 [0], "\tlda\t") &&
1706 LineFullMatch (L2 [1], "\tjsr\tbnega")) {
1708 /* Be sure, X is not used in the load */
1709 if (NoXAddrMode (L2 [0])) {
1711 /* Remove the unnecessary load */
1714 /* L must be valid */
1720 /* Go to the next line */
1721 L = NextCodeLine (L);
1727 static void OptRegLoads (void)
1728 /* Remove unnecessary loads of registers */
1734 /* Repeat this until there is nothing more to delete */
1742 /* Search for a load of X and check if the value is used later */
1743 if (LineMatch (L, "\tldx\t") &&
1745 !IsCondJump (NextInstruction (L))) {
1747 /* Remember to delete this line */
1751 /* Search for a load of A and check if the value is used later */
1752 else if (LineMatch (L, "\tlda\t") &&
1754 !IsCondJump (NextInstruction (L))) {
1756 /* Remember to delete this line */
1760 /* Search for a load of Y and check if the value is used later */
1761 else if (LineMatch (L, "\tldy\t") &&
1763 !IsCondJump (NextInstruction (L))) {
1765 /* Remember to delete this line */
1769 /* Go to the next line, delete the current if requested */
1771 L = NextCodeLine (L);
1777 } while (Deletions > 0);
1782 static int OptPtrOps1 (Line** Start)
1783 /* Optimize several pointer and array constructs - subfunction 1 */
1788 unsigned LinesToRemove;
1793 /* Use a local variable for the working line */
1796 /* Search for (23B/XXT)
1808 * and replace it by something like (24B/26T)
1822 if (!LineMatch (L, "\tlda\t") ||
1823 !GetNextCodeLines (L, L2, 4) ||
1824 !IsLoadAX (L, L2 [0]) ||
1825 !LineFullMatch (L2[1], "\tsta\tregsave") ||
1826 !LineFullMatch (L2[2], "\tstx\tregsave+1")) {
1833 if (LineMatch (L2[3], "\tjsr\tincax")) {
1834 /* Get next code lines */
1835 if (GetNextCodeLines (L2[3], &L2[4], 4) == 0) {
1836 /* Cannot get lines */
1839 Inc = GetHexNum (L2[3]->Line+10);
1843 /* Get next code lines */
1844 if (GetNextCodeLines (L2[3], &L2[4], 7) == 0) {
1845 /* Cannot get lines */
1848 if (LineFullMatch (L2[3], "\tclc") &&
1849 LineMatch (L2[4], "\tadc\t#$") &&
1850 LineFullMatch (L2[5], "\tbcc\t*+3") &&
1851 LineFullMatch (L2[6], "\tinx")) {
1852 /* Inlined increment */
1853 Inc = GetHexNum (L2[4]->Line+7);
1862 /* Check for the remainder */
1863 if (!LineMatch (L3[0], "\tsta\t") ||
1864 strcmp (L3[0]->Line+5, L->Line+5) != 0 ||
1865 !LineMatch (L3[1], "\tstx\t") ||
1866 strcmp (L3[1]->Line+5, L2[0]->Line+5) != 0 ||
1867 !LineFullMatch (L3[2], "\tlda\tregsave") ||
1868 !LineFullMatch (L3[3], "\tldx\tregsave+1")) {
1874 /* Check if AX is actually used following the code above. If not,
1875 * we don't need to load A/X from regsave. Since X will never by
1876 * used without A, check just for A.
1879 if (!RegAUsed (L3[3])) {
1880 /* We don't need to load regsave */
1884 /* Special code for register variables */
1886 if (LineMatch (L, "\tlda\tregbank+") &&
1887 GetNextCodeLines (L3[3], &L3[4], 1) &&
1890 /* Remember the offset into the register bank */
1892 strcpy (Reg, L->Line+5);
1894 /* Check for several special sequences */
1895 if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
1896 /* Load char indirect */
1897 L = ReplaceLine (L, "\tldx\t#$00");
1898 L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
1899 L = NewLineAfter (L, "\tinc\t%s", Reg);
1900 L = NewLineAfter (L, "\tbne\t*+4");
1901 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1904 } else if (LineFullMatch (L3[4], "\tsta\tptr1") &&
1905 GetNextCodeLines (L3[4], &L3[5], 3) &&
1906 LineFullMatch (L3[5], "\tstx\tptr1+1") &&
1907 LineFullMatch (L3[6], "\tldx\t#$00") &&
1908 LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
1910 /* Load char indirect, inlined */
1911 L = ReplaceLine (L, "\tldx\t#$00");
1912 L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
1913 L = NewLineAfter (L, "\tinc\t%s", Reg);
1914 L = NewLineAfter (L, "\tbne\t*+4");
1915 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1919 } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
1920 if (GetNextCodeLines (L3[4], &L3[5], 2) &&
1921 LineMatch (L3[5], "\tlda\t") &&
1922 LineFullMatch (L3[6], "\tjsr\tstaspp")) {
1924 /* Store to pointer */
1925 L = ReplaceLine (L, L3[5]->Line);
1926 L = NewLineAfter (L, "\tldy\t#$00");
1927 L = NewLineAfter (L, "\tsta\t(%s),y", Reg);
1928 L = NewLineAfter (L, "\tinc\t%s", Reg);
1929 L = NewLineAfter (L, "\tbne\t*+4");
1930 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1935 } else if (GetNextCodeLines (L3[4], &L3[5], 3) &&
1936 LineMatch (L3[5], "\tldy\t#$") &&
1937 LineFullMatch (L3[6], "\tlda\t(sp),y") &&
1938 LineFullMatch (L3[7], "\tjsr\tstaspp")) {
1940 /* Beware: We have to correct the stack offset, since we will
1941 * remove the pushax instruction!
1943 Offs = GetHexNum (L3[5]->Line+7) - 2;
1945 /* Store to pointer */
1946 L = ReplaceLine (L, "\tldy\t#$%02X", Offs);
1947 L = NewLineAfter (L, "\tldx\t#$00");
1948 L = NewLineAfter (L, "\tlda\t(sp),y");
1949 L = NewLineAfter (L, "\tsta\t(%s,x)", Reg);
1950 L = NewLineAfter (L, "\tinc\t%s", Reg);
1951 L = NewLineAfter (L, "\tbne\t*+4");
1952 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1962 /* No register variable - insert the first part of the code */
1964 L = NewLineAfter (L, "\tsta\tptr1");
1966 L = NewLineAfter (L, "\tclc");
1967 L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
1968 L = NewLineAfter (L, "\tsta\t%s", L3[0]->Line+5);
1969 L = NewLineAfter (L, "\tlda\t%s", L3[1]->Line+5);
1971 L = NewLineAfter (L, "\tsta\tptr1+1");
1973 L = NewLineAfter (L, "\tadc\t#$00");
1974 L = NewLineAfter (L, "\tsta\t%s", L3[1]->Line+5);
1976 /* Check if we must really load the old value into a/x or if the
1977 * code may be replaced by something else.
1979 if (GetNextCodeLines (L3[3], &L3[4], 1)) {
1980 if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
1981 /* Load char indirect */
1982 L = NewLineAfter (L, "\tldx\t#$00");
1983 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
1986 } else if (LineFullMatch (L3[4], "\tsta\tptr1") &&
1987 GetNextCodeLines (L3[4], &L3[5], 3) &&
1988 LineFullMatch (L3[5], "\tstx\tptr1+1") &&
1989 LineFullMatch (L3[6], "\tldx\t#$00") &&
1990 LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
1992 /* Load char indirect, inlined */
1993 L = NewLineAfter (L, "\tldx\t#$00");
1994 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
1998 } else if (LineFullMatch (L3[4], "\tjsr\tldaxi")) {
1999 /* Load word indirect */
2000 L = NewLineAfter (L, "\tldy\t#$01");
2001 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2002 L = NewLineAfter (L, "\ttax");
2003 L = NewLineAfter (L, "\tdey");
2004 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2008 } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
2009 if (GetNextCodeLines (L3[4], &L3[5], 2) &&
2010 LineMatch (L3[5], "\tlda\t") &&
2011 LineFullMatch (L3[6], "\tjsr\tstaspp")) {
2013 /* Store to pointer */
2014 L = NewLineAfter (L, L3[5]->Line);
2015 L = NewLineAfter (L, "\tldy\t#$00");
2016 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2020 } else if (GetNextCodeLines (L3[4], &L3[5], 3) &&
2021 LineMatch (L3[5], "\tldy\t#$") &&
2022 LineFullMatch (L3[6], "\tlda\t(sp),y") &&
2023 LineFullMatch (L3[7], "\tjsr\tstaspp")) {
2025 /* Beware: We have to correct the stack offset, since we will
2026 * remove the pushax instruction!
2028 sprintf (L3[5]->Line+7, "%02X", GetHexNum (L3[5]->Line+7)-2);
2030 /* Store to pointer */
2031 L = NewLineAfter (L, L3[5]->Line);
2032 L = NewLineAfter (L, L3[6]->Line);
2033 L = NewLineAfter (L, "\tldy\t#$00");
2034 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2043 /* If we need to load a/x, add the code */
2045 L = NewLineAfter (L, "\ttax");
2046 L = NewLineAfter (L, "\tlda\tptr1");
2050 /* Remove the code that is no longer needed */
2051 FreeLines (L2[0], L2[LinesToRemove-1]);
2053 /* Return the new line and success */
2054 *Start = NextCodeLine (L);
2060 static int OptPtrOps2 (Line** Start)
2061 /* Optimize several pointer and array constructs - subfunction 2 */
2066 unsigned LinesToRemove;
2071 /* Use a local variable for the working line */
2074 /* Same as subfunction 1 but for local variables. */
2075 if (LineMatch (L, "\tldy\t#$") == 0) {
2079 /* Get the stack offset. The offset points to the high byte, correct that. */
2080 Offs = GetHexNum (L->Line+7) - 1;
2082 /* Check for the actual sequences */
2083 if (GetNextCodeLines (L, L2, 7) &&
2084 LineFullMatch (L2[0], "\tjsr\tldaxysp") &&
2085 LineFullMatch (L2[1], "\tsta\tregsave") &&
2086 LineFullMatch (L2[2], "\tstx\tregsave+1") &&
2087 LineMatch (L2[3], "\tjsr\tincax")) {
2089 /* Non inlined version */
2090 Inc = GetHexNum (L2[3]->Line+10);
2092 /* Check for stack offset zero */
2093 if (LineFullMatch (L2[4], "\tjsr\tstax0sp") &&
2094 LineFullMatch (L2[5], "\tlda\tregsave") &&
2095 LineFullMatch (L2[6], "\tldx\tregsave+1")) {
2099 } else if (GetNextCodeLines (L2[6], &L2[7], 1) &&
2100 LineMatch (L2[4], "\tldy\t#$") &&
2101 GetHexNum (L2[4]->Line+7) == Offs &&
2102 LineFullMatch (L2[5], "\tjsr\tstaxysp") &&
2103 LineFullMatch (L2[6], "\tlda\tregsave") &&
2104 LineFullMatch (L2[7], "\tldx\tregsave+1")) {
2113 } else if (GetNextCodeLines (L, L2, 13) &&
2114 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
2115 LineFullMatch (L2[1], "\ttax") &&
2116 LineFullMatch (L2[2], "\tdey") &&
2117 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
2118 LineFullMatch (L2[4], "\tsta\tregsave") &&
2119 LineFullMatch (L2[5], "\tstx\tregsave+1") &&
2120 LineFullMatch (L2[6], "\tclc") &&
2121 LineMatch (L2[7], "\tadc\t#$") &&
2122 LineFullMatch (L2[8], "\tbcc\t*+3") &&
2123 LineFullMatch (L2[9], "\tinx")) {
2125 /* Inlined version */
2126 Inc = GetHexNum (L2[7]->Line+7);
2128 /* Check for stack offset zero */
2129 if (LineFullMatch (L2[10], "\tjsr\tstax0sp") &&
2130 LineFullMatch (L2[11], "\tlda\tregsave") &&
2131 LineFullMatch (L2[12], "\tldx\tregsave+1")) {
2135 } else if (GetNextCodeLines (L2[12], &L2[13], 1) &&
2136 LineMatch (L2[10], "\tldy\t#$") &&
2137 GetHexNum (L2[10]->Line+7) == Offs &&
2138 LineFullMatch (L2[11], "\tjsr\tstaxysp") &&
2139 LineFullMatch (L2[12], "\tlda\tregsave") &&
2140 LineFullMatch (L2[13], "\tldx\tregsave+1")) {
2153 /* Get a pointer to the last line of the preceding sequence */
2154 L3 = &L2[LinesToRemove-1];
2156 /* Check if AX is actually used following the code above. If not,
2157 * we don't need to load A/X from regsave. Since X will never by
2158 * used without A, check just for A.
2161 if (!RegAUsed (L3[0])) {
2162 /* We don't need to load regsave */
2166 /* Replace the ldy instruction, offset must point to the low byte */
2167 sprintf (L->Line+7, "%02X", Offs);
2169 /* Insert the first part of the code */
2170 L = NewLineAfter (L, "\tlda\t(sp),y");
2172 L = NewLineAfter (L, "\tsta\tptr1");
2174 L = NewLineAfter (L, "\tclc");
2175 L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
2176 L = NewLineAfter (L, "\tsta\t(sp),y");
2177 L = NewLineAfter (L, "\tiny");
2178 L = NewLineAfter (L, "\tlda\t(sp),y");
2180 L = NewLineAfter (L, "\tsta\tptr1+1");
2182 L = NewLineAfter (L, "\tadc\t#$00");
2183 L = NewLineAfter (L, "\tsta\t(sp),y");
2185 /* Check if we must really load the old value into a/x or if the
2186 * code may be replaced by something else.
2188 if (GetNextCodeLines (L3[0], &L3[1], 1)) {
2189 if (LineFullMatch (L3[1], "\tjsr\tldaui")) {
2190 /* Load char indirect */
2191 L = NewLineAfter (L, "\tldx\t#$00");
2192 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2195 } else if (LineFullMatch (L3[1], "\tsta\tptr1") &&
2196 GetNextCodeLines (L3[1], &L3[2], 3) &&
2197 LineFullMatch (L3[2], "\tstx\tptr1+1") &&
2198 LineFullMatch (L3[3], "\tldx\t#$00") &&
2199 LineFullMatch (L3[4], "\tlda\t(ptr1,x)")) {
2201 /* Load char indirect, inlined */
2202 L = NewLineAfter (L, "\tldx\t#$00");
2203 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2207 } else if (LineFullMatch (L3[1], "\tjsr\tldaxi")) {
2208 /* Load word indirect */
2209 L = NewLineAfter (L, "\tldy\t#$01");
2210 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2211 L = NewLineAfter (L, "\ttax");
2212 L = NewLineAfter (L, "\tdey");
2213 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2217 } else if (LineFullMatch (L3[1], "\tjsr\tpushax")) {
2218 if (GetNextCodeLines (L3[1], &L3[2], 2) &&
2219 LineMatch (L3[2], "\tlda\t") &&
2220 LineFullMatch (L3[3], "\tjsr\tstaspp")) {
2222 /* Store to pointer */
2223 L = NewLineAfter (L, L3[2]->Line);
2224 L = NewLineAfter (L, "\tldy\t#$00");
2225 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2229 } else if (GetNextCodeLines (L3[1], &L3[2], 3) &&
2230 LineMatch (L3[2], "\tldy\t#$") &&
2231 LineFullMatch (L3[3], "\tlda\t(sp),y") &&
2232 LineFullMatch (L3[4], "\tjsr\tstaspp")) {
2234 /* Beware: We have to correct the stack offset, since we will
2235 * remove the pushax instruction!
2237 sprintf (L3[2]->Line+7, "%02X", GetHexNum (L3[2]->Line+7)-2);
2239 /* Store to pointer */
2240 L = NewLineAfter (L, L3[2]->Line);
2241 L = NewLineAfter (L, L3[3]->Line);
2242 L = NewLineAfter (L, "\tldy\t#$00");
2243 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2252 /* If we need to load a/x, add the code */
2254 L = NewLineAfter (L, "\ttax");
2255 L = NewLineAfter (L, "\tlda\tptr1");
2258 /* Remove the code that is no longer needed */
2259 FreeLines (L2[0], L2[LinesToRemove-1]);
2261 /* Return the new line and success */
2262 *Start = NextCodeLine (L);
2268 static void OptPtrOps (void)
2269 /* Optimize several pointer and array constructs */
2273 Line* L = FirstCode;
2276 if (OptPtrOps1 (&L)) {
2278 } else if (OptPtrOps2 (&L)) {
2282 /* Search for the following sequence:
2290 * and replace it by:
2297 else if (LineFullMatch (L, "\tlda\tregsave") && /* Match on start */
2298 GetNextCodeLines (L, L2, 4) && /* Fetch next lines */
2299 LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */
2300 LineFullMatch (L2 [1], "\tjsr\tpushax") &&
2301 LineMatch (L2 [2], "\tlda\t#$") &&
2302 LineFullMatch (L2 [3], "\tjsr\tstaspp")) {
2304 /* Found the sequence, replace it */
2305 L = ReplaceLine (L, L2 [2]->Line); /* lda #$.. */
2306 L2 [0] = ReplaceLine (L2 [0], "\tldy\t#$00");
2307 L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
2309 /* Free the remaining lines */
2310 FreeLines (L2 [2], L2 [3]);
2313 /* Search for the following sequence:
2319 * and replace it by:
2325 else if (LineFullMatch (L, "\tlda\tregsave") && /* Match on start */
2326 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
2327 LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */
2328 LineFullMatch (L2 [1], "\tjsr\tldaui")) {
2330 /* Found the sequence, replace it */
2331 L = ReplaceLine (L, "\tldx\t#$00");
2332 L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
2334 /* Free the remaining lines */
2339 * Search for the following sequence:
2348 * and replace it by:
2359 else if (LineFullMatch (L, "\tlda\tregsave") &&
2360 GetNextCodeLines (L, L2, 5) &&
2361 LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
2362 LineFullMatch (L2 [1], "\tjsr\tpushax") &&
2363 LineMatch (L2 [2], "\tldx\t#$") &&
2364 LineMatch (L2 [3], "\tlda\t#$") &&
2365 LineFullMatch (L2 [4], "\tjsr\tstaxspp")) {
2367 /* Found the sequence, replace it */
2368 L = ReplaceLine (L, "\tldy\t#$01");
2369 L2 [0] = ReplaceLine (L2 [0], L2 [2]->Line);
2370 L2 [0]->Line [3] = 'a';
2371 L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
2372 L2 [4] = ReplaceLine (L2 [4], L2 [3]->Line);
2373 L2 [2] = ReplaceLine (L2 [2], "\ttax");
2374 L2 [3] = ReplaceLine (L2 [3], "\tdey");
2375 L = NewLineAfter (L2 [4], "\tsta\t(regsave),y");
2379 * Search for the following sequence:
2388 * and replace it by:
2394 else if (LineFullMatch (L, "\tlda\tregsave") &&
2395 GetNextCodeLines (L, L2, 5) &&
2396 LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
2397 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2398 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2399 LineFullMatch (L2 [3], "\tldx\t#$00") &&
2400 LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
2402 /* Found the sequence, replace it */
2403 L = ReplaceLine (L, "\tldx\t#$00");
2404 L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
2406 /* Remove the remaining lines */
2407 FreeLines (L2 [1], L2 [4]);
2410 /* Search for the following sequence:
2416 * and replace it by:
2425 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2426 GetNextCodeLines (L, L2, 2) &&
2427 LineMatch (L2 [0], "\tlda\t") &&
2428 LineFullMatch (L2 [1], "\tjsr\tstaspp")) {
2430 /* Found the sequence, replace it */
2431 L = ReplaceLine (L, "\tsta\tptr1");
2432 L2 [1] = ReplaceLine (L2 [1], L2 [0]->Line); /* lda ... */
2433 L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
2434 L2 [2] = NewLineAfter (L2 [1], "\tldy\t#$00");
2435 L = NewLineAfter (L2 [2], "\tsta\t(ptr1),y");
2438 /* Search for the following sequence:
2445 * and replace it by:
2454 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2455 GetNextCodeLines (L, L2, 3) &&
2456 LineMatch (L2 [0], "\tlda\t") &&
2457 LineMatch (L2 [1], "\tldy\t#$") &&
2458 LineFullMatch (L2 [2], "\tjsr\tstaspidx")) {
2460 /* Found the sequence, replace it */
2461 L = ReplaceLine (L, "\tsta\tptr1");
2462 L = NewLineAfter (L, "\tstx\tptr1+1");
2463 L2 [2] = ReplaceLine (L2 [2], "\tsta\t(ptr1),y");
2466 /* Search for the following sequence:
2473 * and replace it by:
2482 * Beware: Since we remove a call to a function that changes the stack
2483 * pointer, we have to adjust the stack address for the lda.
2486 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2487 GetNextCodeLines (L, L2, 3) &&
2488 LineMatch (L2 [0], "\tldy\t#$") &&
2489 LineFullMatch (L2 [1], "\tlda\t(sp),y") &&
2490 LineFullMatch (L2 [2], "\tjsr\tstaspp")) {
2492 /* Found the sequence, replace it. First create a new load
2493 * instruction for the changed stack offset.
2496 sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
2497 L = ReplaceLine (L, "\tsta\tptr1");
2498 L2 [1] = ReplaceLine (L2 [1], Buf); /* ldy ... */
2499 L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
2500 L2 [2] = ReplaceLine (L2 [2], "\tlda\t(sp),y");
2501 L2 [3] = NewLineAfter (L2 [2], "\tldy\t#$00");
2502 L = NewLineAfter (L2 [3], "\tsta\t(ptr1),y");
2505 /* Search for the following sequence:
2513 * and replace it by:
2522 * Beware: Since we remove a call to a function that changes the stack
2523 * pointer, we have to adjust the stack address for the lda.
2526 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2527 GetNextCodeLines (L, L2, 4) &&
2528 LineMatch (L2 [0], "\tldy\t#$") &&
2529 LineFullMatch (L2 [1], "\tlda\t(sp),y") &&
2530 LineMatch (L2 [2], "\tldy\t#$") &&
2531 LineFullMatch (L2 [3], "\tjsr\tstaspidx")) {
2533 /* Found the sequence, replace it. First create a new load
2534 * instruction for the changed stack offset.
2537 sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
2538 L = ReplaceLine (L, "\tsta\tptr1");
2539 L = NewLineAfter (L, "\tstx\tptr1+1");
2540 L2 [0] = ReplaceLine (L2 [0], Buf); /* ldy ... */
2541 L2 [3] = ReplaceLine (L2 [3], "\tsta\t(ptr1),y");
2544 /* Search for the following sequence:
2557 * and replace it by:
2565 * The load of X may be omitted if X is not used below.
2567 else if (LineMatch (L, "\tldax\t_") &&
2568 GetNextCodeLines (L, L2, 9) &&
2569 LineMatch (L2 [0], "\tldy\t#$") &&
2570 LineFullMatch (L2 [1], "\tclc") &&
2571 LineFullMatch (L2 [2], "\tadc\t(sp),y") &&
2572 LineFullMatch (L2 [3], "\tbcc\t*+3") &&
2573 LineFullMatch (L2 [4], "\tinx") &&
2574 LineFullMatch (L2 [5], "\tsta\tptr1") &&
2575 LineFullMatch (L2 [6], "\tstx\tptr1+1") &&
2576 LineFullMatch (L2 [7], "\tldx\t#$00") &&
2577 LineFullMatch (L2 [8], "\tlda\t(ptr1,x)")) {
2579 /* Found the sequence, replace it */
2581 strcpy (Label, L->Line + 6); /* Remember the label */
2582 L = ReplaceLine (L, L2 [0]->Line); /* ldy .. */
2583 L = NewLineAfter (L, "\tlda\t(sp),y");
2584 L = NewLineAfter (L, "\ttay");
2585 if (RegXUsed (L2[8])) {
2586 L = NewLineAfter (L, "\tldx\t#$00");
2588 L = NewLineAfter (L, "\tlda\t%s,y", Label);
2590 /* Remove the remaining stuff. There may be hints between the
2591 * instructions, remove them too
2593 FreeLines (L2[0], L2 [8]);
2617 * This change will cost 2 cycles, but it saves a lot of code (6 bytes
2618 * per occurrence), so we will accept the overhead. It may even be
2619 * possible to rewrite the library routine to get rid of the additional
2622 else if (LineMatch (L, "\tldy\t#$") &&
2623 GetNextCodeLines (L, L2, 6) &&
2624 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
2625 LineFullMatch (L2 [1], "\ttax") &&
2626 LineFullMatch (L2 [2], "\tdey") &&
2627 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
2628 LineMatch (L2 [4], "\tldy\t#$") &&
2629 LineFullMatch (L2 [5], "\tjsr\tldauidx")) {
2631 /* Found - replace it */
2632 L2 [4]->Line [3] = 'x'; /* Change to ldx */
2633 if (LineFullMatch (L, "\tldy\t#$01")) {
2634 /* Word at offset zero */
2636 L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp");
2638 ReplaceLine (L2 [5], "\tjsr\tldauiysp");
2641 /* Delete the remaining lines */
2642 FreeLines (L2 [0], L2 [3]);
2666 * This change will has an overhead of 10 cycles, but it saves 11(!)
2667 * bytes per invocation. Maybe we should apply only if FavourSize is
2670 else if (LineMatch (L, "\tldy\t#$") &&
2671 GetNextCodeLines (L, L2, 8) &&
2672 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
2673 LineFullMatch (L2 [1], "\ttax") &&
2674 LineFullMatch (L2 [2], "\tdey") &&
2675 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
2676 LineFullMatch (L2 [4], "\tsta\tptr1") &&
2677 LineFullMatch (L2 [5], "\tstx\tptr1+1") &&
2678 LineFullMatch (L2 [6], "\tldx\t#$00") &&
2679 LineFullMatch (L2 [7], "\tlda\t(ptr1,x)")) {
2681 /* Found - replace it */
2682 if (LineFullMatch (L, "\tldy\t#$01")) {
2683 /* Word at offset zero */
2685 L = ReplaceLine (L2 [0], "\tjsr\tldau00sp");
2687 ReplaceLine (L2 [0], "\tjsr\tldau0ysp");
2690 /* Delete the remaining lines */
2691 FreeLines (L2 [1], L2 [7]);
2695 L = NextCodeLine (L);
2701 static void OptRegVars (void)
2702 /* Optimize register variable uses */
2706 Line* L = FirstCode;
2709 /* Search for the following sequence:
2715 * and replace it by:
2721 if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2722 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
2723 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2724 LineFullMatch (L2 [1], "\tjsr\tldaui") &&
2725 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2726 strcmp (L2 [0]->Line + 14, "+1") == 0) {
2729 sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
2731 /* Found the sequence, replace it */
2732 L = ReplaceLine (L, "\tldx\t#$00");
2733 L2 [0] = ReplaceLine (L2 [0], Buf);
2735 /* Free the remaining lines */
2739 /* Search for the following sequence:
2748 * and replace it by:
2754 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2755 GetNextCodeLines (L, L2, 5) && /* Fetch next lines */
2756 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2757 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2758 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2759 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2760 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2761 LineFullMatch (L2 [3], "\tldx\t#$00") &&
2762 LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
2765 sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
2767 /* Found the sequence, replace it */
2768 L = ReplaceLine (L, "\tldx\t#$00");
2769 L2 [0] = ReplaceLine (L2 [0], Buf);
2771 /* Free the remaining lines */
2772 FreeLines (L2 [1], L2 [4]);
2775 /* Search for the following sequence:
2782 * and replace it by:
2789 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2790 GetNextCodeLines (L, L2, 3) && /* Fetch next lines */
2791 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2792 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2793 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2794 LineMatch (L2 [1], "\tldy\t#$") &&
2795 LineFullMatch (L2 [2], "\tjsr\tldauidx")) {
2798 sprintf (Buf, "\tlda\t(%s),y", L->Line + 5);
2800 /* Found the sequence, replace it */
2801 L = ReplaceLine (L, L2 [1]->Line);
2802 L2 [0] = ReplaceLine (L2 [0], "\tldx\t#$00");
2803 L2 [1] = ReplaceLine (L2 [1], Buf);
2805 /* Free the remaining lines */
2809 /* Search for the following sequence:
2819 * and replace it by:
2825 * The source form is not generated by the parser but by the optimizer.
2827 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2828 GetNextCodeLines (L, L2, 6) && /* Fetch next lines */
2829 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2830 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2831 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2832 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2833 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2834 LineMatch (L2 [3], "\tlda\t") &&
2835 LineMatch (L2 [4], "\tldy\t#$") &&
2836 LineMatch (L2 [5], "\tsta\t(ptr1),y")) {
2839 sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
2841 /* Found the sequence, replace it */
2842 L2 [5] = ReplaceLine (L2 [5], Buf);
2844 /* Free the remaining lines */
2845 FreeLines (L, L2 [2]);
2847 /* Make the line pointer valid again */
2851 /* Search for the following sequence:
2862 * and replace it by:
2869 * The source form is not generated by the parser but by the optimizer.
2871 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2872 GetNextCodeLines (L, L2, 7) && /* Fetch next lines */
2873 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2874 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2875 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2876 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2877 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2878 LineMatch (L2 [3], "\tldy\t#$") &&
2879 LineFullMatch (L2 [4], "\tlda\t(sp),y") &&
2880 LineMatch (L2 [5], "\tldy\t#$") &&
2881 LineMatch (L2 [6], "\tsta\t(ptr1),y")) {
2884 sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
2886 /* Found the sequence, replace it */
2887 L2 [6] = ReplaceLine (L2 [6], Buf);
2889 /* Free the remaining lines */
2890 FreeLines (L, L2 [2]);
2892 /* Make the line pointer valid again */
2897 L = NextCodeLine (L);
2903 static void OptDoubleJumps (void)
2904 /* Remove/rearrange jumps that jump to other jumps */
2906 static const char* Jumps [] = {
2917 Line* L = FirstCode;
2922 /* Is this a jump? */
2923 while ((I = LineMatchX (L, Jumps)) >= 0) {
2925 /* Yes. Get the target label */
2926 Line* Target = GetTargetLine (L->Line + 5);
2928 /* Target points to the label itself. Skip lines until we reach
2929 * one that is not a label.
2931 Target = NextInstruction (Target);
2933 /* Be sure, this line is not the same as the one the jump is
2934 * in (this happens if there is an empty loop).
2940 if (LineMatch (Target, "\tjmp\t")) {
2942 /* The target is itself a jump. If this is a short branch, get
2943 * the final target and check if it is in reach. Bail out if
2946 if (L->Line[1] == 'b') {
2947 Line* FinalTarget = GetTargetLine (Target->Line+5);
2948 FinalTarget = NextInstruction (FinalTarget);
2949 if ((D = GetJumpDistance (L, FinalTarget)) >= 123) {
2954 /* Make sure the jump does indeed point to another label.
2955 * It may happen that this is not the case for some endless
2956 * loop (while(1) and similar).
2958 if (strcmp (L->Line+5, Target->Line+5) == 0) {
2959 /* Same label, bail out */
2963 /* Use the label in the original jump instead */
2964 L = ReplaceLine (L, "%.5s%s", L->Line, Target->Line+5);
2966 } else if (I < 2 && LineMatch (Target, Jumps [I])) {
2968 /* Conditional jump. Use final label */
2969 strcpy (L->Line+5, Target->Line+5);
2977 L = NextCodeLine (L);
2983 static void OptJumpRTS (void)
2984 /* Replace jumps to an RTS by an RTS */
2986 Line* L = FirstCode;
2988 /* Is this a jump to a numbered label? */
2989 if (LineMatch (L, "\tjmp\t") && L->Line [5] == 'L' && isdigit (L->Line [6])) {
2991 /* Yes. Get the target label */
2992 Line* Target = GetTargetLine (L->Line+5);
2994 /* Target points to the label itself. Get the next line */
2995 Target = NextCodeLine (Target);
2996 if (LineFullMatch (Target, "\trts")) {
2997 /* Replace the jump by an RTS */
2998 L = ReplaceLine (L, "\trts");
3001 L = NextCodeLine (L);
3007 static void OptBoolTransforms (void)
3008 /* Try to remove the boolean transformation subroutines where they aren't
3014 const char* BranchTarget;
3016 Line* L = FirstCode;
3019 /* Search for a boolean transformer followed by a conditional jump. */
3020 if (LineMatch (L, "\tjsr\tbool") &&
3021 GetNextCodeLines (L, L2, 1) &&
3022 IsCondJump (L2 [0])) {
3024 /* Make the boolean transformer unnecessary by changing the
3025 * the conditional jump to evaluate the condition flags that
3026 * are set after the compare directly. Note: jeq jumps if
3027 * the condition is not met, jne jumps if the condition is met.
3030 /* Get the condition code */
3031 int Cond = FindCond (L->Line + 9);
3037 /* Invert the code if we jump on condition not met. */
3038 if (L2[0]->Line [2] == 'e' && L2[0]->Line [3] == 'q') {
3039 /* Jumps if condition false, invert condition */
3040 Cond = CmpInvertTab [Cond];
3043 /* For easier reading, get a pointer to the jump target */
3044 BranchTarget = L2[0]->Line+5;
3046 /* Check if we can replace the jump (sometimes we would need two
3047 * conditional jumps, we will not handle that for now since it
3048 * has some complications - both jumps may be far jumps for
3049 * example making the jumps more costly than the bool transformer
3050 * subroutine). If we cannot replace the jump, bail out.
3055 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3059 L = ReplaceLine (L, "\tjne\t%s", BranchTarget);
3063 Label = AllocLabel ();
3064 L = ReplaceLine (L, "\tbeq\tL%04X", Label);
3065 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3066 L = NewLabelAfter(L, Label);
3070 L = ReplaceLine (L, "\tjpl\t%s", BranchTarget);
3074 L = ReplaceLine (L, "\tjmi\t%s", BranchTarget);
3078 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3079 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3083 Label = AllocLabel ();
3084 L = ReplaceLine (L, "\tbeq\tL%04X", Label);
3085 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3086 L = NewLabelAfter(L, Label);
3090 L = ReplaceLine (L, "\tjcs\t%s", BranchTarget);
3094 L = ReplaceLine (L, "\tjcc\t%s", BranchTarget);
3098 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3099 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3103 Internal ("Unknown jump condition: %u", Cond);
3107 /* Remove the old stuff */
3113 L = NextCodeLine (L);
3119 static void OptCompares2 (void)
3120 /* Try to optimize the integer compare subroutines. */
3124 const char* BranchTarget;
3127 Line* L = FirstCode;
3145 if (LineMatch (L, "\tlda\t") &&
3146 GetNextCodeLines (L, L2, 5) &&
3147 IsLoadAX (L, L2[0]) &&
3148 LineFullMatch (L2[1], "\tcpx\t#$00") &&
3149 LineFullMatch (L2[2], "\tbne\t*+4") &&
3150 LineFullMatch (L2[3], "\tcmp\t#$00") &&
3151 IsCondJump (L2[4])) {
3153 /* Replace the load of X by an ora */
3154 L2[0]->Line[1] = 'o';
3155 L2[0]->Line[2] = 'r';
3156 L2[0]->Line[3] = 'a';
3158 /* Remove unneeded stuff */
3159 FreeLines (L2[1], L2[3]);
3163 /* Same for local variables: Replace
3183 else if (LineMatch (L, "\tldy\t#$") &&
3184 GetNextCodeLines (L, L2, 8) &&
3185 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
3186 LineFullMatch (L2[1], "\ttax") &&
3187 LineFullMatch (L2[2], "\tdey") &&
3188 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
3189 LineFullMatch (L2[4], "\tcpx\t#$00") &&
3190 LineFullMatch (L2[5], "\tbne\t*+4") &&
3191 LineFullMatch (L2[6], "\tcmp\t#$00") &&
3192 IsCondJump (L2[7])) {
3194 /* Replace the second load by an ora */
3195 L2[3]->Line[1] = 'o';
3196 L2[3]->Line[2] = 'r';
3197 L2[3]->Line[3] = 'a';
3199 /* Remove unneeded stuff */
3201 FreeLines (L2[4], L2[6]);
3205 /* Search for the call to a compare subroutine followed by a
3208 else if (LineMatch (L, "\tjsr\ttos") &&
3209 (L2[0] = NextCodeLine (L)) != 0 &&
3210 IsCondJump (L2[0])) {
3212 /* Extract the condition from the function name and branch */
3213 C = CheckAndGetIntCmp (L, L2[0]);
3215 /* Something is wrong */
3219 /* Replace the subroutine call by a cheaper one */
3220 L = ReplaceLine (L, "\tjsr\ttosicmp");
3222 /* For easier reading, get a pointer to the jump target */
3223 BranchTarget = L2[0]->Line+5;
3225 /* Check if we can replace the jump (sometimes we would need two
3226 * conditional jumps, we will not handle that for now since it
3227 * has some complications - both jumps may be far jumps for
3228 * example making the jumps more costly than the bool transformer
3229 * subroutine). If we cannot replace the jump, bail out.
3234 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3238 L = NewLineAfter (L, "\tjne\t%s", BranchTarget);
3242 Label = AllocLabel ();
3243 L = NewLineAfter (L, "\tbeq\tL%04X", Label);
3244 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3245 L = NewLabelAfter(L, Label);
3249 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3253 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3257 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3258 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3262 Label = AllocLabel ();
3263 L = NewLineAfter (L, "\tbeq\tL%04X", Label);
3264 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3265 L = NewLabelAfter(L, Label);
3269 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3273 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3277 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3278 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3282 Internal ("Unknown jump condition: %u", C);
3286 /* Remove the old stuff */
3291 L = NextCodeLine (L);
3297 static void OptTests (void)
3298 /* Remove unnecessary tests */
3302 const char* BitOps [] = {
3309 /* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
3316 Line* L = FirstCode;
3319 /* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
3326 if ((LineMatch (L, "\tlda\t") ||
3327 LineMatch (L, "\tand\t") ||
3328 LineMatch (L, "\tora\t") ||
3329 LineMatch (L, "\teor\t")) &&
3330 GetNextCodeLines (L, L2, 2) &&
3331 (LineFullMatch (L2 [0], "\ttay") ||
3332 LineFullMatch (L2 [0], "\tcmp\t#$00")) &&
3333 IsCondJump (L2 [1])) {
3335 /* We can remove the tay */
3346 * and remove the tax.
3348 else if (LineMatchX (L, BitOps) >= 0 &&
3349 GetNextCodeLines (L, L2, 2) &&
3350 LineFullMatch (L2[0], "\ttax") &&
3351 IsCondJump (L2[1])) {
3353 /* Remove the tax including a hint line of there is one */
3354 if (LineFullMatch (L2[0]->Prev, "+forcetest")) {
3355 FreeLine (L2[0]->Prev);
3359 /* If the line before L loads X, this is useless and may be removed */
3360 L2[0] = PrevCodeLine (L);
3361 if (LineFullMatch (L2[0], "\tldx\t#$00")) {
3367 /* Search for the sequence
3378 else if (LineMatch (L, "\tstx\t") &&
3379 GetNextCodeLines (L, L2, 2) &&
3380 LineFullMatch (L2[0], "\tstx\ttmp1") &&
3381 LineFullMatch (L2[1], "\tora\ttmp1")) {
3383 /* Found, replace it */
3384 L = NewLineAfter (L, "\tora\t%s", L->Line+5);
3386 /* Remove remaining stuff */
3387 FreeLines (L2[0], L2[1]);
3393 L = NextCodeLine (L);
3399 static void OptNeg (void)
3400 /* Optimize the "bnegax/jeq" and "bnegax/jne" sequences */
3404 Line* L = FirstCode;
3407 /* Search for the sequence:
3413 * and replace it by:
3418 if (LineMatch (L, "\tlda\t") && /* Match on start */
3419 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
3420 LineFullMatch (L2 [0], "\tjsr\tbnega") &&
3421 IsCondJump (L2 [1])) {
3423 /* Found the sequence, replace it */
3425 InvertZJump (L2 [1]);
3429 /* Search for the sequence:
3447 else if (LineMatch (L, "\tldy\t#$") &&
3448 GetNextCodeLines (L, L2, 6) &&
3449 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
3450 LineFullMatch (L2[1], "\ttax") &&
3451 LineFullMatch (L2[2], "\tdey") &&
3452 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
3453 LineFullMatch (L2[4], "\tjsr\tbnegax") &&
3454 IsCondJump (L2[5])) {
3456 L2[1] = ReplaceLine (L2[1], "\tdey");
3457 L2[2] = ReplaceLine (L2[2], "\tora\t(sp),y");
3458 FreeLines (L2[3], L2[4]);
3459 InvertZJump (L2[5]);
3463 /* Search for the sequence:
3476 else if (LineMatch (L, "\tlda\t") &&
3477 GetNextCodeLines (L, L2, 3) &&
3478 IsLoadAX (L, L2[0]) &&
3479 LineFullMatch (L2[1], "\tjsr\tbnegax") &&
3480 IsCondJump (L2[2])) {
3482 /* Replace the load of X by ora */
3483 L2[0]->Line[1] = 'o';
3484 L2[0]->Line[2] = 'r';
3485 L2[0]->Line[3] = 'a';
3487 InvertZJump (L2[2]);
3491 /* Search for the sequence:
3497 * and replace it by:
3503 else if (LineMatch (L, "\tjsr\t_") && /* Match on start */
3504 GetNextCodeLines (L, L2, 2) &&
3505 LineMatch (L2 [0], "\tjsr\tbnega") &&
3506 IsCondJump (L2 [1])) {
3508 if (LineFullMatch (L2 [0], "\tjsr\tbnega")) {
3510 L2 [0] = ReplaceLine (L2 [0], "\ttax"); /* Test a */
3513 L2 [0] = ReplaceLine (L2 [0], "\tstx\ttmp1");
3514 NewLineAfter (L2 [0], "\tora\ttmp1");
3517 /* Invert the jump */
3518 InvertZJump (L2 [1]);
3523 L = NextCodeLine (L);
3529 static void OptTriples (void)
3530 /* Replace code triples */
3532 static const char* Pat1 [] = {
3540 static const char* Pat2 [] = {
3548 static const char* Replace [] = {
3556 Line* L = FirstCode;
3558 int I = LineFullMatchX (L, Pat1);
3560 /* We found the first match, get the next line */
3561 Line* L2 = NextCodeLine (L);
3562 if (L2 && LineFullMatch (L2, Pat2 [I])) {
3563 /* Found. Replace by the short call */
3565 L = ReplaceLine (L, Replace [I]);
3569 L = NextCodeLine (L);
3575 static Line* OptOneBlock (Line* L)
3576 /* Optimize the register contents inside one basic block */
3578 static const char* Compares [] = {
3579 "\tjsr\ttoseq00", "\tjsr\ttoseqa0", "\tjsr\ttoseqax",
3580 "\tjsr\ttoseqeax", "\tjsr\ttosne00", "\tjsr\ttosnea0",
3581 "\tjsr\ttosneax", "\tjsr\ttosneeax", "\tjsr\ttoslt00",
3582 "\tjsr\ttoslta0", "\tjsr\ttosltax", "\tjsr\ttosult00",
3583 "\tjsr\ttosulta0", "\tjsr\ttosultax", "\tjsr\ttoslteax",
3584 "\tjsr\ttosulteax", "\tjsr\ttosle00", "\tjsr\ttoslea0",
3585 "\tjsr\ttosleax", "\tjsr\ttosule00", "\tjsr\ttosulea0",
3586 "\tjsr\ttosuleax", "\tjsr\ttosleeax", "\tjsr\ttosuleeax",
3587 "\tjsr\ttosgt00", "\tjsr\ttosgta0", "\tjsr\ttosgtax",
3588 "\tjsr\ttosugt00", "\tjsr\ttosugta0", "\tjsr\ttosugtax",
3589 "\tjsr\ttosgteax", "\tjsr\ttosugteax", "\tjsr\ttosge00",
3590 "\tjsr\ttosgea0", "\tjsr\ttosgeax", "\tjsr\ttosuge00",
3591 "\tjsr\ttosugea0", "\tjsr\ttosugeax", "\tjsr\ttosgeeax",
3596 static const char* MakeBool [] = {
3597 "\tjsr\tbooleq", "\tjsr\tboolne", "\tjsr\tboollt",
3598 "\tjsr\tboolle", "\tjsr\tboolgt", "\tjsr\tboolge",
3599 "\tjsr\tboolult", "\tjsr\tboolule", "\tjsr\tboolugt",
3604 int A = -1; /* Contents of A register */
3605 int X = -1; /* Contents of X register */
3606 int Y = -1; /* Contents of Y register */
3611 while (L && !IsLabel (L)) {
3613 /* Handle all instructions. All instructions not tested here have
3614 * no effects on the register contents.
3617 if (L->Line [0] == '+') {
3618 /* This is a hint */
3619 if (LineMatch (L, "+a:")) {
3620 /* Information about a */
3621 switch (L->Line [3]) {
3622 case '!': A = -1; break;
3623 case '=': A = GetHexNum (L->Line + 4); break;
3625 } else if (LineMatch (L, "+x:")) {
3626 /* The code generator tells something about the x register */
3627 switch (L->Line [3]) {
3628 case '!': X = -1; break;
3629 case '=': X = GetHexNum (L->Line + 4); break;
3631 } else if (LineMatch (L, "+y:")) {
3632 /* Information about the y register */
3633 switch (L->Line [3]) {
3634 case '!': Y = -1; break;
3635 case '=': Y = GetHexNum (L->Line + 4); break;
3638 } else if (LineMatch (L, "\tadc\t")) {
3639 if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
3640 L->Line[strlen(L->Line)-2] = '\0';
3643 } else if (LineMatch (L, "\tand\t")) {
3645 } else if (LineFullMatch (L, "\tasl\ta")) {
3647 A = (A << 1) & 0xFF;
3649 } else if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
3650 L->Line[strlen(L->Line)-2] = '\0';
3651 } else if (CPU == CPU_65C02 && (LineFullMatch (L, "\tdea") ||
3652 LineFullMatch (L, "\tdec\ta"))) {
3654 } else if (LineFullMatch (L, "\tdex")) {
3656 } else if (LineFullMatch (L, "\tdey")) {
3658 } else if (LineMatch (L, "\teor")) {
3660 } else if (CPU == CPU_65C02 && (LineFullMatch (L, "\tina") ||
3661 LineFullMatch (L, "\tinc\ta"))) {
3663 } else if (LineFullMatch (L, "\tinx")) {
3665 } else if (LineFullMatch (L, "\tiny")) {
3667 } else if (LineFullMatch (L, "\tjsr\taddeq0sp")) {
3668 /* We know about this function */
3671 } else if (LineFullMatch (L, "\tjsr\taddeqysp")) {
3672 /* We know about this function */
3675 } else if (LineFullMatch (L, "\tjsr\taxulong")) {
3676 /* We know about this function and we're trying to replace it by
3677 * inline code if we have already a register that contains zero.
3682 } else if (X == 0) {
3684 } else if (Y == 0) {
3690 /* We cannot replace the code, but we know about the results */
3693 L = ReplaceLine (L, "\tst%c\tsreg", C);
3694 NewLineAfter (L, "\tst%c\tsreg+1", C);
3696 } else if (LineFullMatch (L, "\tjsr\tbnega")) {
3697 /* We know about this function */
3700 } else if (LineFullMatch (L, "\tjsr\tbnegax")) {
3701 /* We know about this function */
3704 } else if (LineFullMatch (L, "\tjsr\tbnegeax")) {
3705 /* We know about this function */
3708 } else if (LineFullMatch (L, "\tjsr\tcomplax")) {
3709 /* We know about this function */
3716 } else if (LineFullMatch (L, "\tjsr\tdecax1")) {
3717 /* We know about this function */
3719 } else if (LineFullMatch (L, "\tjsr\tdecax2")) {
3720 /* We know about this function */
3722 } else if (LineFullMatch (L, "\tjsr\tdecaxy")) {
3723 /* We know about this function */
3725 } else if (LineFullMatch (L, "\tjsr\tdeceaxy")) {
3726 /* We know about this function */
3728 } else if (LineFullMatch (L, "\tjsr\tincax1")) {
3729 /* We know about this function */
3731 } else if (LineFullMatch (L, "\tjsr\tincax2")) {
3732 /* We know about this function */
3734 } else if (LineFullMatch (L, "\tjsr\tinceaxy")) {
3735 /* We know about this function */
3737 } else if (LineFullMatch (L, "\tjsr\tladdeq")) {
3738 /* We know about this function */
3741 } else if (LineFullMatch (L, "\tjsr\tladdeqb")) {
3742 /* We know about this function */
3745 } else if (LineFullMatch (L, "\tjsr\tlbneg")) {
3746 /* We know about this function */
3749 } else if (LineFullMatch (L, "\tjsr\tldai")) {
3750 /* We know about this function */
3753 } else if (LineFullMatch (L, "\tjsr\tldaidx")) {
3754 /* We know about this function */
3756 } else if (LineFullMatch (L, "\tjsr\tldau00sp")) {
3757 /* We know about this function */
3761 } else if (LineFullMatch (L, "\tjsr\tldau0ysp")) {
3762 /* We know about this function */
3766 } else if (LineFullMatch (L, "\tjsr\tldaui")) {
3767 /* We know about this function */
3771 } else if (LineFullMatch (L, "\tjsr\tldaui0sp")) {
3775 } else if (LineFullMatch (L, "\tjsr\tldauidx")) {
3776 /* We know about this function */
3779 } else if (LineFullMatch (L, "\tjsr\tldauiysp")) {
3780 /* We know about this function */
3784 } else if (LineFullMatch (L, "\tjsr\tldax0sp")) {
3785 /* We know about this function */
3788 } else if (LineFullMatch (L, "\tjsr\tldaxi")) {
3789 /* We know about this function */
3792 } else if (LineFullMatch (L, "\tjsr\tldaxidx")) {
3793 /* We know about this function */
3796 } else if (LineFullMatch (L, "\tjsr\tldaxysp")) {
3797 /* We know about this function */
3800 } else if (LineFullMatch (L, "\tjsr\tldeaxi")) {
3801 /* We know about this function */
3804 } else if (LineFullMatch (L, "\tjsr\tldeaxidx")) {
3805 /* We know about this function */
3808 } else if (LineFullMatch (L, "\tjsr\tlsubeq")) {
3809 /* We know about this function */
3812 } else if (LineFullMatch (L, "\tjsr\tlsubeqb")) {
3813 /* We know about this function */
3816 } else if (LineFullMatch (L, "\tjsr\tnegax")) {
3817 /* We know about this function */
3819 } else if (LineFullMatch (L, "\tjsr\tnegeax")) {
3820 /* We know about this function */
3822 } else if (LineFullMatch (L, "\tjsr\tpush0")) {
3823 /* We know about this function */
3827 } else if (LineFullMatch (L, "\tjsr\tpush1")) {
3828 /* We know about this function */
3832 } else if (LineFullMatch (L, "\tjsr\tpush2")) {
3833 /* We know about this function */
3837 } else if (LineFullMatch (L, "\tjsr\tpush3")) {
3838 /* We know about this function */
3842 } else if (LineFullMatch (L, "\tjsr\tpush4")) {
3843 /* We know about this function */
3847 } else if (LineFullMatch (L, "\tjsr\tpush5")) {
3848 /* We know about this function */
3852 } else if (LineFullMatch (L, "\tjsr\tpush6")) {
3853 /* We know about this function */
3857 } else if (LineFullMatch (L, "\tjsr\tpush7")) {
3858 /* We know about this function */
3862 } else if (LineFullMatch (L, "\tjsr\tpusha")) {
3863 /* We know about this function */
3865 } else if (LineFullMatch (L, "\tjsr\tpusha0")) {
3866 /* We know about this function
3867 * If X is already zero, we may call pushax instead and save two
3871 L = ReplaceLine (L, "\tjsr\tpushax");
3875 } else if (LineFullMatch (L, "\tjsr\tpushax")) {
3876 /* We know about this function */
3878 } else if (LineFullMatch (L, "\tjsr\tpushaysp")) {
3879 /* We know about this function */
3882 } else if (LineFullMatch (L, "\tjsr\tpushc0")) {
3883 /* We know about this function */
3886 } else if (LineFullMatch (L, "\tjsr\tpushc1")) {
3887 /* We know about this function */
3890 } else if (LineFullMatch (L, "\tjsr\tpushc2")) {
3891 /* We know about this function */
3894 } else if (LineFullMatch (L, "\tjsr\tpushw")) {
3895 /* We know about this function (calls pushax) */
3898 } else if (LineFullMatch (L, "\tjsr\tpushw0sp")) {
3899 /* We know about this function(calls pushax) */
3902 } else if (LineFullMatch (L, "\tjsr\tpushwidx")) {
3903 /* We know about this function (calls pushax) */
3906 } else if (LineFullMatch (L, "\tjsr\tpushwysp")) {
3907 /* We know about this function (calls pushax) */
3910 } else if (LineFullMatch (L, "\tjsr\tresteax")) {
3911 /* We know about this function */
3913 } else if (LineFullMatch (L, "\tjsr\tsaveeax")) {
3914 /* We know about this function */
3915 /* Changes nothing */
3916 } else if (LineFullMatch (L, "\tjsr\tshrax1")) {
3917 /* We know about this function */
3919 } else if (LineFullMatch (L, "\tjsr\tshrax2")) {
3920 /* We know about this function */
3922 } else if (LineFullMatch (L, "\tjsr\tshrax3")) {
3923 /* We know about this function */
3925 } else if (LineFullMatch (L, "\tjsr\tshreax1")) {
3926 /* We know about this function */
3928 } else if (LineFullMatch (L, "\tjsr\tshreax2")) {
3929 /* We know about this function */
3931 } else if (LineFullMatch (L, "\tjsr\tshreax3")) {
3932 /* We know about this function */
3934 } else if (LineFullMatch (L, "\tjsr\tstaspp")) {
3935 /* We know about this function */
3937 } else if (LineFullMatch (L, "\tjsr\tstaxspp")) {
3938 /* We know about this function */
3940 } else if (LineFullMatch (L, "\tjsr\tstax0sp")) {
3941 /* We know about this function */
3943 } else if (LineFullMatch (L, "\tjsr\tstaxysp")) {
3944 /* We know about this function */
3946 } else if (LineFullMatch (L, "\tjsr\tsubeq0sp")) {
3947 /* We know about this function */
3950 } else if (LineFullMatch (L, "\tjsr\tsubeqysp")) {
3951 /* We know about this function */
3954 } else if (LineFullMatch (L, "\tjsr\ttosadda0")) {
3955 /* We know about this function */
3958 } else if (LineFullMatch (L, "\tjsr\ttosaddax")) {
3959 /* We know about this function */
3962 } else if (LineFullMatch (L, "\tjsr\ttosicmp")) {
3963 /* We know about this function */
3966 } else if (LineFullMatchX (L, Compares) >= 0) {
3969 } else if (LineFullMatchX (L, MakeBool) >= 0) {
3972 } else if (LineMatch (L, "\tjsr\t")) {
3973 /* Subroutine call, forget all register information */
3975 } else if (LineMatch (L, "\tlda\t")) {
3976 if (!RegAUsed (L) && !IsCondJump (NextInstruction (L))) {
3977 /* The value loaded is not used later, remove it */
3979 } else if (LineMatch (L, "\tlda\t(")) {
3980 if (IsXAddrMode (L)) {
3981 /* lda (zp,x) - if Y and X are both zero, replace by
3982 * load indirect y and save one cycle in some cases.
3984 if (X == 0 && Y == 0) {
3986 const char* S = L->Line + 6;
3988 strcpy (Buf, "\tlda\t(");
3996 L = ReplaceLine (L, Buf);
3999 /* In any case invalidate A */
4001 } else if (LineMatch (L, "\tlda\t#$")) {
4002 /* Immidiate load into A */
4003 NewVal = GetHexNum (L->Line + 7);
4005 /* Load has no effect */
4007 } else if (NewVal == X) {
4008 /* Requested value is already in X */
4009 L = ReplaceLine (L, "\ttxa");
4010 } else if (NewVal == Y) {
4011 /* Requested value is already in Y */
4012 L = ReplaceLine (L, "\ttya");
4013 } else if (CPU == CPU_65C02 && A != -1) {
4014 /* Try ina/dea operators of 65C02 */
4015 if (NewVal == ((A - 1) & 0xFF)) {
4016 L = ReplaceLine (L, "\tdea");
4017 } else if (NewVal == ((A + 1) & 0xFF)) {
4018 L = ReplaceLine (L, "\tina");
4021 /* Anyway, the new value is now in A */
4024 /* Memory load into A */
4027 } else if (LineMatch (L, "\tldax\t")) {
4028 /* Memory load into A and X */
4030 } else if (LineMatch (L, "\tldx\t")) {
4031 if (!RegXUsed (L) && !IsCondJump (NextInstruction (L))) {
4032 /* The value loaded is not used later, remove it */
4034 } else if (LineMatch (L, "\tldx\t#$")) {
4035 /* Immidiate load into X */
4036 NewVal = GetHexNum (L->Line + 7);
4038 /* Load has no effect */
4040 } else if (NewVal == A) {
4041 /* Requested value is already in A */
4042 L = ReplaceLine (L, "\ttax");
4043 } else if (X != -1 && NewVal == ((X + 1) & 0xFF)) {
4044 /* Requested value is one more than current contents */
4045 L = ReplaceLine (L, "\tinx");
4046 } else if (X != -1 && NewVal == ((X - 1) & 0xFF)) {
4047 /* Requested value is one less than current contents */
4048 L = ReplaceLine (L, "\tdex");
4050 /* Anyway, the new value is now in X */
4053 /* Memory load into X */
4056 } else if (LineMatch (L, "\tldy\t")) {
4057 if (!RegYUsed (L) && !IsCondJump (NextInstruction (L))) {
4058 /* The value loaded is not used later, remove it */
4060 } else if (LineMatch (L, "\tldy\t#$")) {
4061 /* Immidiate load into Y */
4062 NewVal = GetHexNum (L->Line + 7);
4064 /* Load has no effect */
4066 } else if (NewVal == A) {
4067 /* Requested value is already in A */
4068 L = ReplaceLine (L, "\ttay");
4069 } else if (Y != -1 && NewVal == ((Y + 1) & 0xFF)) {
4070 /* Requested value is one more than current contents */
4071 L = ReplaceLine (L, "\tiny");
4072 } else if (Y != -1 && NewVal == ((Y - 1) & 0xFF)) {
4073 /* Requested value is one less than current contents */
4074 L = ReplaceLine (L, "\tdey");
4076 /* Anyway, the new value is now in Y */
4079 /* Memory load into Y */
4082 } else if (LineFullMatch (L, "\tlsr\ta")) {
4086 } else if (LineMatch (L, "\tora\t#$")) {
4088 A |= GetHexNum (L->Line + 7);
4090 } else if (LineMatch (L, "\tora\t")) {
4092 } else if (LineFullMatch (L, "\tpla")) {
4094 } else if (LineFullMatch (L, "\trol\ta")) {
4096 } else if (LineFullMatch (L, "\tror\ta")) {
4098 } else if (LineFullMatch (L, "\trts")) {
4100 } else if (LineFullMatch (L, "\trti")) {
4102 } else if (LineMatch (L, "\tsbc\t")) {
4103 if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
4104 L->Line[strlen(L->Line)-2] = '\0';
4107 } else if (CPU == CPU_65C02 && LineMatch (L, "\tst")) {
4108 /* Try to replace by stz if possible */
4109 if (A == 0 && LineMatch (L, "\tsta\t")) {
4110 /* Not indirect and not Y allowed */
4111 if (L->Line[5] != '(' && !IsYAddrMode (L)) {
4114 } else if (X == 0 && LineMatch (L, "\tstx\t")) {
4115 /* absolute,y not allowed */
4116 if (!IsYAddrMode (L)) {
4119 } else if (Y == 0 && LineMatch (L, "\tsty\t")) {
4120 /* sty and stz share all addressing modes */
4123 } else if (LineFullMatch (L, "\ttax")) {
4124 if (A != -1 && X == A) {
4125 /* Load has no effect */
4130 } else if (LineFullMatch (L, "\ttay")) {
4131 if (A != -1 && Y == A) {
4132 /* Load has no effect */
4137 } else if (LineFullMatch (L, "\ttsx")) {
4139 } else if (LineFullMatch (L, "\ttxa")) {
4140 if (X != -1 && A == X) {
4141 /* Load has no effect */
4146 } else if (LineFullMatch (L, "\ttya")) {
4147 if (Y != -1 && A == Y) {
4148 /* Load has no effect */
4155 /* Set to next line, handle deletions */
4156 L2 = NextCodeSegLine (L);
4164 /* Skip the label */
4165 L = NextCodeSegLine (L);
4172 static void OptBlocks (void)
4173 /* Optimize the register contents inside basic blocks */
4175 Line* L = FirstCode;
4177 L = OptOneBlock (L);
4183 static void OptJumps (void)
4184 /* Optimize jumps */
4186 static const char* Jumps [] = {
4196 Line* L = FirstCode;
4198 int I = LineMatchX (L, Jumps);
4200 Line* Target = GetTargetLine (L->Line+5);
4201 if (Target->Index > L->Index) {
4202 /* This is a forward jump. Backward jumps are handled
4203 * automagically by the assembler.
4205 unsigned Distance = GetJumpDistance (L, Target);
4206 if (Distance < 123) { /* Safety */
4207 L->Line [1] = 'b'; /* Make a short branch */
4208 L->Size = 2; /* Set new size */
4212 L = NextCodeLine (L);
4215 /* Special treatment for jumps on the 65C02 */
4216 if (CPU == CPU_65C02) {
4218 Line* L = FirstCode;
4220 if (LineMatch (L, "\tjmp\tL")) {
4221 Line* Target = GetTargetLine (L->Line+5);
4222 unsigned Distance = GetJumpDistance (L, Target);
4223 if (Distance < 123) { /* Safety */
4224 L->Line [1] = 'b'; /* Make a short branch */
4227 L->Size = 2; /* Set new size */
4230 L = NextCodeLine (L);
4238 static void OptRTS (void)
4239 /* Change sequences of jsr XXX/rts to jmp XXX */
4241 Line* L = FirstCode;
4243 if (LineMatch (L, "\tjsr\t")) {
4244 /* This is a jsr, get the next instruction */
4245 Line* L2 = NextCodeLine (L);
4246 if (L2 && LineFullMatch (L2, "\trts")) {
4247 /* We found a sequence */
4253 /* Try the next line */
4254 L = NextCodeLine (L);
4260 /*****************************************************************************/
4262 /*****************************************************************************/
4266 static void OptOnePass (unsigned long Flag, void (*F) (void))
4267 /* Call one optimizer pass if enabled */
4269 if ((OptDisable & Flag) == 0) {
4271 } else if (Verbose || Debug) {
4272 printf ("Optimizer pass %04lX skipped\n", Flag);
4278 void OptDoOpt (void)
4279 /* Run the optimizer over the collected stuff */
4281 /* Find and remember the first line of code */
4284 /* Mark all lines inside the code segment */
4287 /* Create a list of all local labels for fast access */
4290 /* Ok, now start the real optimizations */
4292 /* Optimize compares - first step */
4293 OptOnePass (0x0001, OptCompares1);
4295 /* Remove dead jumps */
4296 OptOnePass (0x0002, OptDeadJumps);
4298 /* Remove unnecessary loads */
4299 OptOnePass (0x0004, OptLoads);
4301 /* Remove unnecessary register loads */
4302 OptOnePass (0x0008, OptRegLoads);
4304 /* Optimize stores through pointers */
4305 OptOnePass (0x0010, OptPtrOps);
4307 /* Optimize use of register variables */
4308 OptOnePass (0x0020, OptRegVars);
4310 /* Remove jump cascades - must be used before OptNeg */
4311 OptOnePass (0x0040, OptDoubleJumps);
4313 /* Remove unnecessary boolean negates */
4314 OptOnePass (0x0080, OptNeg);
4316 /* Replace jumps to an RTS by an RTS */
4317 OptOnePass (0x0100, OptJumpRTS);
4319 /* Optimize boolean transforms */
4320 OptOnePass (0x0200, OptBoolTransforms);
4322 /* Optimize compares */
4323 OptOnePass (0x0400, OptCompares2);
4325 /* Remove unnecessary tests */
4326 OptOnePass (0x0800, OptTests);
4328 /* Optimize several triples */
4329 OptOnePass (0x1000, OptTriples);
4331 /* Optimize basic blocks */
4332 OptOnePass (0x2000, OptBlocks);
4334 /* Remove unnecessary register loads (another pass) */
4335 OptOnePass (0x0008, OptRegLoads);
4337 /* Optimize jumps */
4338 OptOnePass (0x4000, OptJumps);
4340 /* Optimize jsr/rts sequences */
4341 OptOnePass (0x8000, OptRTS);