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 /*****************************************************************************/
41 #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 be
1876 * used without A, check just for A.
1878 NeedLoad = RegAUsed (L3[3]);
1880 /* Special code for register variables */
1882 if (LineMatch (L, "\tlda\tregbank+") &&
1883 GetNextCodeLines (L3[3], &L3[4], 1) &&
1886 /* Remember the offset into the register bank */
1888 strcpy (Reg, L->Line+5);
1890 /* Check for several special sequences */
1891 if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
1892 /* Load char indirect */
1893 L = ReplaceLine (L, "\tldx\t#$00");
1894 L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
1895 L = NewLineAfter (L, "\tinc\t%s", Reg);
1896 L = NewLineAfter (L, "\tbne\t*+4");
1897 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1900 } else if (LineFullMatch (L3[4], "\tsta\tptr1") &&
1901 GetNextCodeLines (L3[4], &L3[5], 3) &&
1902 LineFullMatch (L3[5], "\tstx\tptr1+1") &&
1903 LineFullMatch (L3[6], "\tldx\t#$00") &&
1904 LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
1906 /* Load char indirect, inlined */
1907 L = ReplaceLine (L, "\tldx\t#$00");
1908 L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
1909 L = NewLineAfter (L, "\tinc\t%s", Reg);
1910 L = NewLineAfter (L, "\tbne\t*+4");
1911 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1915 } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
1916 if (GetNextCodeLines (L3[4], &L3[5], 2) &&
1917 LineMatch (L3[5], "\tlda\t") &&
1918 LineFullMatch (L3[6], "\tjsr\tstaspp")) {
1920 /* Store to pointer */
1921 L = ReplaceLine (L, L3[5]->Line);
1922 L = NewLineAfter (L, "\tldy\t#$00");
1923 L = NewLineAfter (L, "\tsta\t(%s),y", Reg);
1924 L = NewLineAfter (L, "\tinc\t%s", Reg);
1925 L = NewLineAfter (L, "\tbne\t*+4");
1926 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1931 } else if (GetNextCodeLines (L3[4], &L3[5], 3) &&
1932 LineMatch (L3[5], "\tldy\t#$") &&
1933 LineFullMatch (L3[6], "\tlda\t(sp),y") &&
1934 LineFullMatch (L3[7], "\tjsr\tstaspp")) {
1936 /* Beware: We have to correct the stack offset, since we will
1937 * remove the pushax instruction!
1939 Offs = GetHexNum (L3[5]->Line+7) - 2;
1941 /* Store to pointer */
1942 L = ReplaceLine (L, "\tldy\t#$%02X", Offs);
1943 L = NewLineAfter (L, "\tldx\t#$00");
1944 L = NewLineAfter (L, "\tlda\t(sp),y");
1945 L = NewLineAfter (L, "\tsta\t(%s,x)", Reg);
1946 L = NewLineAfter (L, "\tinc\t%s", Reg);
1947 L = NewLineAfter (L, "\tbne\t*+4");
1948 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1958 /* No register variable - insert the first part of the code */
1960 L = NewLineAfter (L, "\tsta\tptr1");
1962 L = NewLineAfter (L, "\tclc");
1963 L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
1964 L = NewLineAfter (L, "\tsta\t%s", L3[0]->Line+5);
1965 L = NewLineAfter (L, "\tlda\t%s", L3[1]->Line+5);
1967 L = NewLineAfter (L, "\tsta\tptr1+1");
1969 L = NewLineAfter (L, "\tadc\t#$00");
1970 L = NewLineAfter (L, "\tsta\t%s", L3[1]->Line+5);
1972 /* Check if we must really load the old value into a/x or if the
1973 * code may be replaced by something else.
1975 if (GetNextCodeLines (L3[3], &L3[4], 1)) {
1976 if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
1977 /* Load char indirect */
1978 L = NewLineAfter (L, "\tldx\t#$00");
1979 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
1982 } else if (LineFullMatch (L3[4], "\tsta\tptr1") &&
1983 GetNextCodeLines (L3[4], &L3[5], 3) &&
1984 LineFullMatch (L3[5], "\tstx\tptr1+1") &&
1985 LineFullMatch (L3[6], "\tldx\t#$00") &&
1986 LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
1988 /* Load char indirect, inlined */
1989 L = NewLineAfter (L, "\tldx\t#$00");
1990 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
1994 } else if (LineFullMatch (L3[4], "\tjsr\tldaxi")) {
1995 /* Load word indirect */
1996 L = NewLineAfter (L, "\tldy\t#$01");
1997 L = NewLineAfter (L, "\tlda\t(ptr1),y");
1998 L = NewLineAfter (L, "\ttax");
1999 L = NewLineAfter (L, "\tdey");
2000 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2004 } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
2005 if (GetNextCodeLines (L3[4], &L3[5], 2) &&
2006 LineMatch (L3[5], "\tlda\t") &&
2007 LineFullMatch (L3[6], "\tjsr\tstaspp")) {
2009 /* Store to pointer */
2010 L = NewLineAfter (L, L3[5]->Line);
2011 L = NewLineAfter (L, "\tldy\t#$00");
2012 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2016 } else if (GetNextCodeLines (L3[4], &L3[5], 3) &&
2017 LineMatch (L3[5], "\tldy\t#$") &&
2018 LineFullMatch (L3[6], "\tlda\t(sp),y") &&
2019 LineFullMatch (L3[7], "\tjsr\tstaspp")) {
2021 /* Beware: We have to correct the stack offset, since we will
2022 * remove the pushax instruction!
2024 sprintf (L3[5]->Line+7, "%02X", GetHexNum (L3[5]->Line+7)-2);
2026 /* Store to pointer */
2027 L = NewLineAfter (L, L3[5]->Line);
2028 L = NewLineAfter (L, L3[6]->Line);
2029 L = NewLineAfter (L, "\tldy\t#$00");
2030 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2039 /* If we need to load a/x, add the code */
2041 L = NewLineAfter (L, "\tlda\tptr1");
2042 L = NewLineAfter (L, "\tldx\tptr1+1");
2046 /* Remove the code that is no longer needed */
2047 FreeLines (L2[0], L2[LinesToRemove-1]);
2049 /* Return the new line and success */
2050 *Start = NextCodeLine (L);
2056 static int OptPtrOps2 (Line** Start)
2057 /* Optimize several pointer and array constructs - subfunction 2 */
2062 unsigned LinesToRemove;
2067 /* Use a local variable for the working line */
2070 /* Same as subfunction 1 but for local variables. */
2071 if (LineMatch (L, "\tldy\t#$") == 0) {
2075 /* Get the stack offset. The offset points to the high byte, correct that. */
2076 Offs = GetHexNum (L->Line+7) - 1;
2078 /* Check for the actual sequences */
2079 if (GetNextCodeLines (L, L2, 7) &&
2080 LineFullMatch (L2[0], "\tjsr\tldaxysp") &&
2081 LineFullMatch (L2[1], "\tsta\tregsave") &&
2082 LineFullMatch (L2[2], "\tstx\tregsave+1") &&
2083 LineMatch (L2[3], "\tjsr\tincax")) {
2085 /* Non inlined version */
2086 Inc = GetHexNum (L2[3]->Line+10);
2088 /* Check for stack offset zero */
2089 if (LineFullMatch (L2[4], "\tjsr\tstax0sp") &&
2090 LineFullMatch (L2[5], "\tlda\tregsave") &&
2091 LineFullMatch (L2[6], "\tldx\tregsave+1")) {
2095 } else if (GetNextCodeLines (L2[6], &L2[7], 1) &&
2096 LineMatch (L2[4], "\tldy\t#$") &&
2097 GetHexNum (L2[4]->Line+7) == Offs &&
2098 LineFullMatch (L2[5], "\tjsr\tstaxysp") &&
2099 LineFullMatch (L2[6], "\tlda\tregsave") &&
2100 LineFullMatch (L2[7], "\tldx\tregsave+1")) {
2109 } else if (GetNextCodeLines (L, L2, 13) &&
2110 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
2111 LineFullMatch (L2[1], "\ttax") &&
2112 LineFullMatch (L2[2], "\tdey") &&
2113 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
2114 LineFullMatch (L2[4], "\tsta\tregsave") &&
2115 LineFullMatch (L2[5], "\tstx\tregsave+1") &&
2116 LineFullMatch (L2[6], "\tclc") &&
2117 LineMatch (L2[7], "\tadc\t#$") &&
2118 LineFullMatch (L2[8], "\tbcc\t*+3") &&
2119 LineFullMatch (L2[9], "\tinx")) {
2121 /* Inlined version */
2122 Inc = GetHexNum (L2[7]->Line+7);
2124 /* Check for stack offset zero */
2125 if (LineFullMatch (L2[10], "\tjsr\tstax0sp") &&
2126 LineFullMatch (L2[11], "\tlda\tregsave") &&
2127 LineFullMatch (L2[12], "\tldx\tregsave+1")) {
2131 } else if (GetNextCodeLines (L2[12], &L2[13], 1) &&
2132 LineMatch (L2[10], "\tldy\t#$") &&
2133 GetHexNum (L2[10]->Line+7) == Offs &&
2134 LineFullMatch (L2[11], "\tjsr\tstaxysp") &&
2135 LineFullMatch (L2[12], "\tlda\tregsave") &&
2136 LineFullMatch (L2[13], "\tldx\tregsave+1")) {
2149 /* Get a pointer to the last line of the preceding sequence */
2150 L3 = &L2[LinesToRemove-1];
2152 /* Check if AX is actually used following the code above. If not,
2153 * we don't need to load A/X from regsave. Since X will never by
2154 * used without A, check just for A.
2156 NeedLoad = RegAUsed (L3[0]);
2158 /* Replace the ldy instruction, offset must point to the low byte */
2159 sprintf (L->Line+7, "%02X", Offs);
2161 /* Insert the first part of the code */
2162 L = NewLineAfter (L, "\tlda\t(sp),y");
2164 L = NewLineAfter (L, "\tsta\tptr1");
2166 L = NewLineAfter (L, "\tclc");
2167 L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
2168 L = NewLineAfter (L, "\tsta\t(sp),y");
2169 L = NewLineAfter (L, "\tiny");
2170 L = NewLineAfter (L, "\tlda\t(sp),y");
2172 L = NewLineAfter (L, "\tsta\tptr1+1");
2174 L = NewLineAfter (L, "\tadc\t#$00");
2175 L = NewLineAfter (L, "\tsta\t(sp),y");
2177 /* Check if we must really load the old value into a/x or if the
2178 * code may be replaced by something else.
2180 if (GetNextCodeLines (L3[0], &L3[1], 1)) {
2181 if (LineFullMatch (L3[1], "\tjsr\tldaui")) {
2182 /* Load char indirect */
2183 L = NewLineAfter (L, "\tldx\t#$00");
2184 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2187 } else if (LineFullMatch (L3[1], "\tsta\tptr1") &&
2188 GetNextCodeLines (L3[1], &L3[2], 3) &&
2189 LineFullMatch (L3[2], "\tstx\tptr1+1") &&
2190 LineFullMatch (L3[3], "\tldx\t#$00") &&
2191 LineFullMatch (L3[4], "\tlda\t(ptr1,x)")) {
2193 /* Load char indirect, inlined */
2194 L = NewLineAfter (L, "\tldx\t#$00");
2195 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2199 } else if (LineFullMatch (L3[1], "\tjsr\tldaxi")) {
2200 /* Load word indirect */
2201 L = NewLineAfter (L, "\tldy\t#$01");
2202 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2203 L = NewLineAfter (L, "\ttax");
2204 L = NewLineAfter (L, "\tdey");
2205 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2209 } else if (LineFullMatch (L3[1], "\tjsr\tpushax")) {
2210 if (GetNextCodeLines (L3[1], &L3[2], 2) &&
2211 LineMatch (L3[2], "\tlda\t") &&
2212 LineFullMatch (L3[3], "\tjsr\tstaspp")) {
2214 /* Store to pointer */
2215 L = NewLineAfter (L, L3[2]->Line);
2216 L = NewLineAfter (L, "\tldy\t#$00");
2217 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2221 } else if (GetNextCodeLines (L3[1], &L3[2], 3) &&
2222 LineMatch (L3[2], "\tldy\t#$") &&
2223 LineFullMatch (L3[3], "\tlda\t(sp),y") &&
2224 LineFullMatch (L3[4], "\tjsr\tstaspp")) {
2226 /* Beware: We have to correct the stack offset, since we will
2227 * remove the pushax instruction!
2229 sprintf (L3[2]->Line+7, "%02X", GetHexNum (L3[2]->Line+7)-2);
2231 /* Store to pointer */
2232 L = NewLineAfter (L, L3[2]->Line);
2233 L = NewLineAfter (L, L3[3]->Line);
2234 L = NewLineAfter (L, "\tldy\t#$00");
2235 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2244 /* If we need to load a/x, add the code */
2246 L = NewLineAfter (L, "\tlda\tptr1");
2247 L = NewLineAfter (L, "\tldx\tptr1+1");
2250 /* Remove the code that is no longer needed */
2251 FreeLines (L2[0], L2[LinesToRemove-1]);
2253 /* Return the new line and success */
2254 *Start = NextCodeLine (L);
2260 static void OptPtrOps (void)
2261 /* Optimize several pointer and array constructs */
2265 Line* L = FirstCode;
2268 if (OptPtrOps1 (&L)) {
2270 } else if (OptPtrOps2 (&L)) {
2274 /* Search for the following sequence:
2282 * and replace it by:
2289 else if (LineFullMatch (L, "\tlda\tregsave") && /* Match on start */
2290 GetNextCodeLines (L, L2, 4) && /* Fetch next lines */
2291 LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */
2292 LineFullMatch (L2 [1], "\tjsr\tpushax") &&
2293 LineMatch (L2 [2], "\tlda\t#$") &&
2294 LineFullMatch (L2 [3], "\tjsr\tstaspp")) {
2296 /* Found the sequence, replace it */
2297 L = ReplaceLine (L, L2 [2]->Line); /* lda #$.. */
2298 L2 [0] = ReplaceLine (L2 [0], "\tldy\t#$00");
2299 L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
2301 /* Free the remaining lines */
2302 FreeLines (L2 [2], L2 [3]);
2305 /* Search for the following sequence:
2311 * and replace it by:
2317 else if (LineFullMatch (L, "\tlda\tregsave") && /* Match on start */
2318 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
2319 LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */
2320 LineFullMatch (L2 [1], "\tjsr\tldaui")) {
2322 /* Found the sequence, replace it */
2323 L = ReplaceLine (L, "\tldx\t#$00");
2324 L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
2326 /* Free the remaining lines */
2331 * Search for the following sequence:
2340 * and replace it by:
2351 else if (LineFullMatch (L, "\tlda\tregsave") &&
2352 GetNextCodeLines (L, L2, 5) &&
2353 LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
2354 LineFullMatch (L2 [1], "\tjsr\tpushax") &&
2355 LineMatch (L2 [2], "\tldx\t#$") &&
2356 LineMatch (L2 [3], "\tlda\t#$") &&
2357 LineFullMatch (L2 [4], "\tjsr\tstaxspp")) {
2359 /* Found the sequence, replace it */
2360 L = ReplaceLine (L, "\tldy\t#$01");
2361 L2 [0] = ReplaceLine (L2 [0], L2 [2]->Line);
2362 L2 [0]->Line [3] = 'a';
2363 L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
2364 L2 [4] = ReplaceLine (L2 [4], L2 [3]->Line);
2365 L2 [2] = ReplaceLine (L2 [2], "\ttax");
2366 L2 [3] = ReplaceLine (L2 [3], "\tdey");
2367 L = NewLineAfter (L2 [4], "\tsta\t(regsave),y");
2371 * Search for the following sequence:
2380 * and replace it by:
2386 else if (LineFullMatch (L, "\tlda\tregsave") &&
2387 GetNextCodeLines (L, L2, 5) &&
2388 LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
2389 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2390 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2391 LineFullMatch (L2 [3], "\tldx\t#$00") &&
2392 LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
2394 /* Found the sequence, replace it */
2395 L = ReplaceLine (L, "\tldx\t#$00");
2396 L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
2398 /* Remove the remaining lines */
2399 FreeLines (L2 [1], L2 [4]);
2402 /* Search for the following sequence:
2408 * and replace it by:
2417 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2418 GetNextCodeLines (L, L2, 2) &&
2419 LineMatch (L2 [0], "\tlda\t") &&
2420 LineFullMatch (L2 [1], "\tjsr\tstaspp")) {
2422 /* Found the sequence, replace it */
2423 L = ReplaceLine (L, "\tsta\tptr1");
2424 L2 [1] = ReplaceLine (L2 [1], L2 [0]->Line); /* lda ... */
2425 L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
2426 L2 [2] = NewLineAfter (L2 [1], "\tldy\t#$00");
2427 L = NewLineAfter (L2 [2], "\tsta\t(ptr1),y");
2430 /* Search for the following sequence:
2437 * and replace it by:
2446 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2447 GetNextCodeLines (L, L2, 3) &&
2448 LineMatch (L2 [0], "\tlda\t") &&
2449 LineMatch (L2 [1], "\tldy\t#$") &&
2450 LineFullMatch (L2 [2], "\tjsr\tstaspidx")) {
2452 /* Found the sequence, replace it */
2453 L = ReplaceLine (L, "\tsta\tptr1");
2454 L = NewLineAfter (L, "\tstx\tptr1+1");
2455 L2 [2] = ReplaceLine (L2 [2], "\tsta\t(ptr1),y");
2458 /* Search for the following sequence:
2465 * and replace it by:
2474 * Beware: Since we remove a call to a function that changes the stack
2475 * pointer, we have to adjust the stack address for the lda.
2478 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2479 GetNextCodeLines (L, L2, 3) &&
2480 LineMatch (L2 [0], "\tldy\t#$") &&
2481 LineFullMatch (L2 [1], "\tlda\t(sp),y") &&
2482 LineFullMatch (L2 [2], "\tjsr\tstaspp")) {
2484 /* Found the sequence, replace it. First create a new load
2485 * instruction for the changed stack offset.
2488 sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
2489 L = ReplaceLine (L, "\tsta\tptr1");
2490 L2 [1] = ReplaceLine (L2 [1], Buf); /* ldy ... */
2491 L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
2492 L2 [2] = ReplaceLine (L2 [2], "\tlda\t(sp),y");
2493 L2 [3] = NewLineAfter (L2 [2], "\tldy\t#$00");
2494 L = NewLineAfter (L2 [3], "\tsta\t(ptr1),y");
2497 /* Search for the following sequence:
2505 * and replace it by:
2514 * Beware: Since we remove a call to a function that changes the stack
2515 * pointer, we have to adjust the stack address for the lda.
2518 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2519 GetNextCodeLines (L, L2, 4) &&
2520 LineMatch (L2 [0], "\tldy\t#$") &&
2521 LineFullMatch (L2 [1], "\tlda\t(sp),y") &&
2522 LineMatch (L2 [2], "\tldy\t#$") &&
2523 LineFullMatch (L2 [3], "\tjsr\tstaspidx")) {
2525 /* Found the sequence, replace it. First create a new load
2526 * instruction for the changed stack offset.
2529 sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
2530 L = ReplaceLine (L, "\tsta\tptr1");
2531 L = NewLineAfter (L, "\tstx\tptr1+1");
2532 L2 [0] = ReplaceLine (L2 [0], Buf); /* ldy ... */
2533 L2 [3] = ReplaceLine (L2 [3], "\tsta\t(ptr1),y");
2536 /* Search for the following sequence:
2549 * and replace it by:
2557 * The load of X may be omitted if X is not used below.
2559 else if (LineMatch (L, "\tldax\t_") &&
2560 GetNextCodeLines (L, L2, 9) &&
2561 LineMatch (L2 [0], "\tldy\t#$") &&
2562 LineFullMatch (L2 [1], "\tclc") &&
2563 LineFullMatch (L2 [2], "\tadc\t(sp),y") &&
2564 LineFullMatch (L2 [3], "\tbcc\t*+3") &&
2565 LineFullMatch (L2 [4], "\tinx") &&
2566 LineFullMatch (L2 [5], "\tsta\tptr1") &&
2567 LineFullMatch (L2 [6], "\tstx\tptr1+1") &&
2568 LineFullMatch (L2 [7], "\tldx\t#$00") &&
2569 LineFullMatch (L2 [8], "\tlda\t(ptr1,x)")) {
2571 /* Found the sequence, replace it */
2573 strcpy (Label, L->Line + 6); /* Remember the label */
2574 L = ReplaceLine (L, L2 [0]->Line); /* ldy .. */
2575 L = NewLineAfter (L, "\tlda\t(sp),y");
2576 L = NewLineAfter (L, "\ttay");
2577 if (RegXUsed (L2[8])) {
2578 L = NewLineAfter (L, "\tldx\t#$00");
2580 L = NewLineAfter (L, "\tlda\t%s,y", Label);
2582 /* Remove the remaining stuff. There may be hints between the
2583 * instructions, remove them too
2585 FreeLines (L2[0], L2 [8]);
2609 * This change will cost 2 cycles, but it saves a lot of code (6 bytes
2610 * per occurrence), so we will accept the overhead. It may even be
2611 * possible to rewrite the library routine to get rid of the additional
2614 else if (LineMatch (L, "\tldy\t#$") &&
2615 GetNextCodeLines (L, L2, 6) &&
2616 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
2617 LineFullMatch (L2 [1], "\ttax") &&
2618 LineFullMatch (L2 [2], "\tdey") &&
2619 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
2620 LineMatch (L2 [4], "\tldy\t#$") &&
2621 LineFullMatch (L2 [5], "\tjsr\tldauidx")) {
2623 /* Found - replace it */
2624 L2 [4]->Line [3] = 'x'; /* Change to ldx */
2625 if (LineFullMatch (L, "\tldy\t#$01")) {
2626 /* Word at offset zero */
2628 L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp");
2630 ReplaceLine (L2 [5], "\tjsr\tldauiysp");
2633 /* Delete the remaining lines */
2634 FreeLines (L2 [0], L2 [3]);
2658 * This change will has an overhead of 10 cycles, but it saves 11(!)
2659 * bytes per invocation. Maybe we should apply only if FavourSize is
2662 else if (LineMatch (L, "\tldy\t#$") &&
2663 GetNextCodeLines (L, L2, 8) &&
2664 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
2665 LineFullMatch (L2 [1], "\ttax") &&
2666 LineFullMatch (L2 [2], "\tdey") &&
2667 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
2668 LineFullMatch (L2 [4], "\tsta\tptr1") &&
2669 LineFullMatch (L2 [5], "\tstx\tptr1+1") &&
2670 LineFullMatch (L2 [6], "\tldx\t#$00") &&
2671 LineFullMatch (L2 [7], "\tlda\t(ptr1,x)")) {
2673 /* Found - replace it */
2674 if (LineFullMatch (L, "\tldy\t#$01")) {
2675 /* Word at offset zero */
2677 L = ReplaceLine (L2 [0], "\tjsr\tldau00sp");
2679 ReplaceLine (L2 [0], "\tjsr\tldau0ysp");
2682 /* Delete the remaining lines */
2683 FreeLines (L2 [1], L2 [7]);
2687 L = NextCodeLine (L);
2693 static void OptRegVars (void)
2694 /* Optimize register variable uses */
2698 Line* L = FirstCode;
2701 /* Search for the following sequence:
2707 * and replace it by:
2713 if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2714 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
2715 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2716 LineFullMatch (L2 [1], "\tjsr\tldaui") &&
2717 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2718 strcmp (L2 [0]->Line + 14, "+1") == 0) {
2721 sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
2723 /* Found the sequence, replace it */
2724 L = ReplaceLine (L, "\tldx\t#$00");
2725 L2 [0] = ReplaceLine (L2 [0], Buf);
2727 /* Free the remaining lines */
2731 /* Search for the following sequence:
2740 * and replace it by:
2746 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2747 GetNextCodeLines (L, L2, 5) && /* Fetch next lines */
2748 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2749 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2750 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2751 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2752 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2753 LineFullMatch (L2 [3], "\tldx\t#$00") &&
2754 LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
2757 sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
2759 /* Found the sequence, replace it */
2760 L = ReplaceLine (L, "\tldx\t#$00");
2761 L2 [0] = ReplaceLine (L2 [0], Buf);
2763 /* Free the remaining lines */
2764 FreeLines (L2 [1], L2 [4]);
2767 /* Search for the following sequence:
2774 * and replace it by:
2781 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2782 GetNextCodeLines (L, L2, 3) && /* Fetch next lines */
2783 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2784 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2785 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2786 LineMatch (L2 [1], "\tldy\t#$") &&
2787 LineFullMatch (L2 [2], "\tjsr\tldauidx")) {
2790 sprintf (Buf, "\tlda\t(%s),y", L->Line + 5);
2792 /* Found the sequence, replace it */
2793 L = ReplaceLine (L, L2 [1]->Line);
2794 L2 [0] = ReplaceLine (L2 [0], "\tldx\t#$00");
2795 L2 [1] = ReplaceLine (L2 [1], Buf);
2797 /* Free the remaining lines */
2801 /* Search for the following sequence:
2811 * and replace it by:
2817 * The source form is not generated by the parser but by the optimizer.
2819 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2820 GetNextCodeLines (L, L2, 6) && /* Fetch next lines */
2821 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2822 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2823 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2824 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2825 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2826 LineMatch (L2 [3], "\tlda\t") &&
2827 LineMatch (L2 [4], "\tldy\t#$") &&
2828 LineMatch (L2 [5], "\tsta\t(ptr1),y")) {
2831 sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
2833 /* Found the sequence, replace it */
2834 L2 [5] = ReplaceLine (L2 [5], Buf);
2836 /* Free the remaining lines */
2837 FreeLines (L, L2 [2]);
2839 /* Make the line pointer valid again */
2843 /* Search for the following sequence:
2854 * and replace it by:
2861 * The source form is not generated by the parser but by the optimizer.
2863 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2864 GetNextCodeLines (L, L2, 7) && /* Fetch next lines */
2865 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2866 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2867 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2868 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2869 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2870 LineMatch (L2 [3], "\tldy\t#$") &&
2871 LineFullMatch (L2 [4], "\tlda\t(sp),y") &&
2872 LineMatch (L2 [5], "\tldy\t#$") &&
2873 LineMatch (L2 [6], "\tsta\t(ptr1),y")) {
2876 sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
2878 /* Found the sequence, replace it */
2879 L2 [6] = ReplaceLine (L2 [6], Buf);
2881 /* Free the remaining lines */
2882 FreeLines (L, L2 [2]);
2884 /* Make the line pointer valid again */
2889 L = NextCodeLine (L);
2895 static void OptDoubleJumps (void)
2896 /* Remove/rearrange jumps that jump to other jumps */
2898 static const char* Jumps [] = {
2909 Line* L = FirstCode;
2914 /* Is this a jump? */
2915 while ((I = LineMatchX (L, Jumps)) >= 0) {
2917 /* Yes. Get the target label */
2918 Line* Target = GetTargetLine (L->Line + 5);
2920 /* Target points to the label itself. Skip lines until we reach
2921 * one that is not a label.
2923 Target = NextInstruction (Target);
2925 /* Be sure, this line is not the same as the one the jump is
2926 * in (this happens if there is an empty loop).
2932 if (LineMatch (Target, "\tjmp\t")) {
2934 /* The target is itself a jump. If this is a short branch, get
2935 * the final target and check if it is in reach. Bail out if
2938 if (L->Line[1] == 'b') {
2939 Line* FinalTarget = GetTargetLine (Target->Line+5);
2940 FinalTarget = NextInstruction (FinalTarget);
2941 if ((D = GetJumpDistance (L, FinalTarget)) >= 123) {
2946 /* Make sure the jump does indeed point to another label.
2947 * It may happen that this is not the case for some endless
2948 * loop (while(1) and similar).
2950 if (strcmp (L->Line+5, Target->Line+5) == 0) {
2951 /* Same label, bail out */
2955 /* Use the label in the original jump instead */
2956 L = ReplaceLine (L, "%.5s%s", L->Line, Target->Line+5);
2958 } else if (I < 2 && LineMatch (Target, Jumps [I])) {
2960 /* Conditional jump. Use final label */
2961 strcpy (L->Line+5, Target->Line+5);
2969 L = NextCodeLine (L);
2975 static void OptJumpRTS (void)
2976 /* Replace jumps to an RTS by an RTS */
2978 Line* L = FirstCode;
2980 /* Is this a jump to a numbered label? */
2981 if (LineMatch (L, "\tjmp\t") && L->Line [5] == 'L' && isdigit (L->Line [6])) {
2983 /* Yes. Get the target label */
2984 Line* Target = GetTargetLine (L->Line+5);
2986 /* Target points to the label itself. Get the next line */
2987 Target = NextCodeLine (Target);
2988 if (LineFullMatch (Target, "\trts")) {
2989 /* Replace the jump by an RTS */
2990 L = ReplaceLine (L, "\trts");
2993 L = NextCodeLine (L);
2999 static void OptBoolTransforms (void)
3000 /* Try to remove the boolean transformation subroutines where they aren't
3006 const char* BranchTarget;
3008 Line* L = FirstCode;
3011 /* Search for a boolean transformer followed by a conditional jump. */
3012 if (LineMatch (L, "\tjsr\tbool") &&
3013 GetNextCodeLines (L, L2, 1) &&
3014 IsCondJump (L2 [0])) {
3016 /* Make the boolean transformer unnecessary by changing the
3017 * the conditional jump to evaluate the condition flags that
3018 * are set after the compare directly. Note: jeq jumps if
3019 * the condition is not met, jne jumps if the condition is met.
3022 /* Get the condition code */
3023 int Cond = FindCond (L->Line + 9);
3029 /* Invert the code if we jump on condition not met. */
3030 if (L2[0]->Line [2] == 'e' && L2[0]->Line [3] == 'q') {
3031 /* Jumps if condition false, invert condition */
3032 Cond = CmpInvertTab [Cond];
3035 /* For easier reading, get a pointer to the jump target */
3036 BranchTarget = L2[0]->Line+5;
3038 /* Check if we can replace the jump (sometimes we would need two
3039 * conditional jumps, we will not handle that for now since it
3040 * has some complications - both jumps may be far jumps for
3041 * example making the jumps more costly than the bool transformer
3042 * subroutine). If we cannot replace the jump, bail out.
3047 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3051 L = ReplaceLine (L, "\tjne\t%s", BranchTarget);
3055 Label = AllocLabel ();
3056 L = ReplaceLine (L, "\tbeq\tL%04X", Label);
3057 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3058 L = NewLabelAfter(L, Label);
3062 L = ReplaceLine (L, "\tjpl\t%s", BranchTarget);
3066 L = ReplaceLine (L, "\tjmi\t%s", BranchTarget);
3070 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3071 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3075 Label = AllocLabel ();
3076 L = ReplaceLine (L, "\tbeq\tL%04X", Label);
3077 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3078 L = NewLabelAfter(L, Label);
3082 L = ReplaceLine (L, "\tjcs\t%s", BranchTarget);
3086 L = ReplaceLine (L, "\tjcc\t%s", BranchTarget);
3090 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3091 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3095 Internal ("Unknown jump condition: %u", Cond);
3099 /* Remove the old stuff */
3105 L = NextCodeLine (L);
3111 static void OptCompares2 (void)
3112 /* Try to optimize the integer compare subroutines. */
3116 const char* BranchTarget;
3119 Line* L = FirstCode;
3137 if (LineMatch (L, "\tlda\t") &&
3138 GetNextCodeLines (L, L2, 5) &&
3139 IsLoadAX (L, L2[0]) &&
3140 LineFullMatch (L2[1], "\tcpx\t#$00") &&
3141 LineFullMatch (L2[2], "\tbne\t*+4") &&
3142 LineFullMatch (L2[3], "\tcmp\t#$00") &&
3143 IsCondJump (L2[4])) {
3145 /* Replace the load of X by an ora */
3146 L2[0]->Line[1] = 'o';
3147 L2[0]->Line[2] = 'r';
3148 L2[0]->Line[3] = 'a';
3150 /* Remove unneeded stuff */
3151 FreeLines (L2[1], L2[3]);
3155 /* Same for local variables: Replace
3175 else if (LineMatch (L, "\tldy\t#$") &&
3176 GetNextCodeLines (L, L2, 8) &&
3177 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
3178 LineFullMatch (L2[1], "\ttax") &&
3179 LineFullMatch (L2[2], "\tdey") &&
3180 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
3181 LineFullMatch (L2[4], "\tcpx\t#$00") &&
3182 LineFullMatch (L2[5], "\tbne\t*+4") &&
3183 LineFullMatch (L2[6], "\tcmp\t#$00") &&
3184 IsCondJump (L2[7])) {
3186 /* Replace the second load by an ora */
3187 L2[3]->Line[1] = 'o';
3188 L2[3]->Line[2] = 'r';
3189 L2[3]->Line[3] = 'a';
3191 /* Remove unneeded stuff */
3193 FreeLines (L2[4], L2[6]);
3197 /* Search for the call to a compare subroutine followed by a
3200 else if (LineMatch (L, "\tjsr\ttos") &&
3201 (L2[0] = NextCodeLine (L)) != 0 &&
3202 IsCondJump (L2[0])) {
3204 /* Extract the condition from the function name and branch */
3205 C = CheckAndGetIntCmp (L, L2[0]);
3207 /* Something is wrong */
3211 /* Replace the subroutine call by a cheaper one */
3212 L = ReplaceLine (L, "\tjsr\ttosicmp");
3214 /* For easier reading, get a pointer to the jump target */
3215 BranchTarget = L2[0]->Line+5;
3217 /* Check if we can replace the jump (sometimes we would need two
3218 * conditional jumps, we will not handle that for now since it
3219 * has some complications - both jumps may be far jumps for
3220 * example making the jumps more costly than the bool transformer
3221 * subroutine). If we cannot replace the jump, bail out.
3226 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3230 L = NewLineAfter (L, "\tjne\t%s", BranchTarget);
3234 Label = AllocLabel ();
3235 L = NewLineAfter (L, "\tbeq\tL%04X", Label);
3236 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3237 L = NewLabelAfter(L, Label);
3241 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3245 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3249 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3250 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3254 Label = AllocLabel ();
3255 L = NewLineAfter (L, "\tbeq\tL%04X", Label);
3256 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3257 L = NewLabelAfter(L, Label);
3261 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3265 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3269 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3270 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3274 Internal ("Unknown jump condition: %u", C);
3278 /* Remove the old stuff */
3283 L = NextCodeLine (L);
3289 static void OptTests (void)
3290 /* Remove unnecessary tests */
3294 const char* BitOps [] = {
3301 /* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
3308 Line* L = FirstCode;
3311 /* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
3318 if ((LineMatch (L, "\tlda\t") ||
3319 LineMatch (L, "\tand\t") ||
3320 LineMatch (L, "\tora\t") ||
3321 LineMatch (L, "\teor\t")) &&
3322 GetNextCodeLines (L, L2, 2) &&
3323 (LineFullMatch (L2 [0], "\ttay") ||
3324 LineFullMatch (L2 [0], "\tcmp\t#$00")) &&
3325 IsCondJump (L2 [1])) {
3327 /* We can remove the tay */
3338 * and remove the tax.
3340 else if (LineMatchX (L, BitOps) >= 0 &&
3341 GetNextCodeLines (L, L2, 2) &&
3342 LineFullMatch (L2[0], "\ttax") &&
3343 IsCondJump (L2[1])) {
3345 /* Remove the tax including a hint line of there is one */
3346 if (LineFullMatch (L2[0]->Prev, "+forcetest")) {
3347 FreeLine (L2[0]->Prev);
3351 /* If the line before L loads X, this is useless and may be removed */
3352 L2[0] = PrevCodeLine (L);
3353 if (LineFullMatch (L2[0], "\tldx\t#$00")) {
3359 /* Search for the sequence
3370 else if (LineMatch (L, "\tstx\t") &&
3371 GetNextCodeLines (L, L2, 2) &&
3372 LineFullMatch (L2[0], "\tstx\ttmp1") &&
3373 LineFullMatch (L2[1], "\tora\ttmp1")) {
3375 /* Found, replace it */
3376 L = NewLineAfter (L, "\tora\t%s", L->Line+5);
3378 /* Remove remaining stuff */
3379 FreeLines (L2[0], L2[1]);
3385 L = NextCodeLine (L);
3391 static void OptNeg (void)
3392 /* Optimize the "bnegax/jeq" and "bnegax/jne" sequences */
3396 Line* L = FirstCode;
3399 /* Search for the sequence:
3405 * and replace it by:
3410 if (LineMatch (L, "\tlda\t") && /* Match on start */
3411 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
3412 LineFullMatch (L2 [0], "\tjsr\tbnega") &&
3413 IsCondJump (L2 [1])) {
3415 /* Found the sequence, replace it */
3417 InvertZJump (L2 [1]);
3421 /* Search for the sequence:
3439 else if (LineMatch (L, "\tldy\t#$") &&
3440 GetNextCodeLines (L, L2, 6) &&
3441 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
3442 LineFullMatch (L2[1], "\ttax") &&
3443 LineFullMatch (L2[2], "\tdey") &&
3444 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
3445 LineFullMatch (L2[4], "\tjsr\tbnegax") &&
3446 IsCondJump (L2[5])) {
3448 L2[1] = ReplaceLine (L2[1], "\tdey");
3449 L2[2] = ReplaceLine (L2[2], "\tora\t(sp),y");
3450 FreeLines (L2[3], L2[4]);
3451 InvertZJump (L2[5]);
3455 /* Search for the sequence:
3468 else if (LineMatch (L, "\tlda\t") &&
3469 GetNextCodeLines (L, L2, 3) &&
3470 IsLoadAX (L, L2[0]) &&
3471 LineFullMatch (L2[1], "\tjsr\tbnegax") &&
3472 IsCondJump (L2[2])) {
3474 /* Replace the load of X by ora */
3475 L2[0]->Line[1] = 'o';
3476 L2[0]->Line[2] = 'r';
3477 L2[0]->Line[3] = 'a';
3479 InvertZJump (L2[2]);
3483 /* Search for the sequence:
3489 * and replace it by:
3495 else if (LineMatch (L, "\tjsr\t_") && /* Match on start */
3496 GetNextCodeLines (L, L2, 2) &&
3497 LineMatch (L2 [0], "\tjsr\tbnega") &&
3498 IsCondJump (L2 [1])) {
3500 if (LineFullMatch (L2 [0], "\tjsr\tbnega")) {
3502 L2 [0] = ReplaceLine (L2 [0], "\ttax"); /* Test a */
3505 L2 [0] = ReplaceLine (L2 [0], "\tstx\ttmp1");
3506 NewLineAfter (L2 [0], "\tora\ttmp1");
3509 /* Invert the jump */
3510 InvertZJump (L2 [1]);
3515 L = NextCodeLine (L);
3521 static void OptTriples (void)
3522 /* Replace code triples */
3524 static const char* Pat1 [] = {
3532 static const char* Pat2 [] = {
3540 static const char* Replace [] = {
3548 Line* L = FirstCode;
3550 int I = LineFullMatchX (L, Pat1);
3552 /* We found the first match, get the next line */
3553 Line* L2 = NextCodeLine (L);
3554 if (L2 && LineFullMatch (L2, Pat2 [I])) {
3555 /* Found. Replace by the short call */
3557 L = ReplaceLine (L, Replace [I]);
3561 L = NextCodeLine (L);
3567 static Line* OptOneBlock (Line* L)
3568 /* Optimize the register contents inside one basic block */
3570 static const char* Compares [] = {
3571 "\tjsr\ttoseq00", "\tjsr\ttoseqa0", "\tjsr\ttoseqax",
3572 "\tjsr\ttoseqeax", "\tjsr\ttosne00", "\tjsr\ttosnea0",
3573 "\tjsr\ttosneax", "\tjsr\ttosneeax", "\tjsr\ttoslt00",
3574 "\tjsr\ttoslta0", "\tjsr\ttosltax", "\tjsr\ttosult00",
3575 "\tjsr\ttosulta0", "\tjsr\ttosultax", "\tjsr\ttoslteax",
3576 "\tjsr\ttosulteax", "\tjsr\ttosle00", "\tjsr\ttoslea0",
3577 "\tjsr\ttosleax", "\tjsr\ttosule00", "\tjsr\ttosulea0",
3578 "\tjsr\ttosuleax", "\tjsr\ttosleeax", "\tjsr\ttosuleeax",
3579 "\tjsr\ttosgt00", "\tjsr\ttosgta0", "\tjsr\ttosgtax",
3580 "\tjsr\ttosugt00", "\tjsr\ttosugta0", "\tjsr\ttosugtax",
3581 "\tjsr\ttosgteax", "\tjsr\ttosugteax", "\tjsr\ttosge00",
3582 "\tjsr\ttosgea0", "\tjsr\ttosgeax", "\tjsr\ttosuge00",
3583 "\tjsr\ttosugea0", "\tjsr\ttosugeax", "\tjsr\ttosgeeax",
3588 static const char* MakeBool [] = {
3589 "\tjsr\tbooleq", "\tjsr\tboolne", "\tjsr\tboollt",
3590 "\tjsr\tboolle", "\tjsr\tboolgt", "\tjsr\tboolge",
3591 "\tjsr\tboolult", "\tjsr\tboolule", "\tjsr\tboolugt",
3596 int A = -1; /* Contents of A register */
3597 int X = -1; /* Contents of X register */
3598 int Y = -1; /* Contents of Y register */
3603 while (L && !IsLabel (L)) {
3605 /* Handle all instructions. All instructions not tested here have
3606 * no effects on the register contents.
3609 if (L->Line [0] == '+') {
3610 /* This is a hint */
3611 if (LineMatch (L, "+a:")) {
3612 /* Information about a */
3613 switch (L->Line [3]) {
3614 case '!': A = -1; break;
3615 case '=': A = GetHexNum (L->Line + 4); break;
3617 } else if (LineMatch (L, "+x:")) {
3618 /* The code generator tells something about the x register */
3619 switch (L->Line [3]) {
3620 case '!': X = -1; break;
3621 case '=': X = GetHexNum (L->Line + 4); break;
3623 } else if (LineMatch (L, "+y:")) {
3624 /* Information about the y register */
3625 switch (L->Line [3]) {
3626 case '!': Y = -1; break;
3627 case '=': Y = GetHexNum (L->Line + 4); break;
3630 } else if (LineMatch (L, "\tadc\t")) {
3631 if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
3632 L->Line[strlen(L->Line)-2] = '\0';
3635 } else if (LineMatch (L, "\tand\t")) {
3637 } else if (LineFullMatch (L, "\tasl\ta")) {
3639 A = (A << 1) & 0xFF;
3641 } else if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
3642 L->Line[strlen(L->Line)-2] = '\0';
3643 } else if (CPU == CPU_65C02 && (LineFullMatch (L, "\tdea") ||
3644 LineFullMatch (L, "\tdec\ta"))) {
3646 } else if (LineFullMatch (L, "\tdex")) {
3648 } else if (LineFullMatch (L, "\tdey")) {
3650 } else if (LineMatch (L, "\teor")) {
3652 } else if (CPU == CPU_65C02 && (LineFullMatch (L, "\tina") ||
3653 LineFullMatch (L, "\tinc\ta"))) {
3655 } else if (LineFullMatch (L, "\tinx")) {
3657 } else if (LineFullMatch (L, "\tiny")) {
3659 } else if (LineFullMatch (L, "\tjsr\taddeq0sp")) {
3660 /* We know about this function */
3663 } else if (LineFullMatch (L, "\tjsr\taddeqysp")) {
3664 /* We know about this function */
3667 } else if (LineFullMatch (L, "\tjsr\taxulong")) {
3668 /* We know about this function and we're trying to replace it by
3669 * inline code if we have already a register that contains zero.
3674 } else if (X == 0) {
3676 } else if (Y == 0) {
3682 /* We cannot replace the code, but we know about the results */
3685 L = ReplaceLine (L, "\tst%c\tsreg", C);
3686 NewLineAfter (L, "\tst%c\tsreg+1", C);
3688 } else if (LineFullMatch (L, "\tjsr\tbnega")) {
3689 /* We know about this function */
3692 } else if (LineFullMatch (L, "\tjsr\tbnegax")) {
3693 /* We know about this function */
3696 } else if (LineFullMatch (L, "\tjsr\tbnegeax")) {
3697 /* We know about this function */
3700 } else if (LineFullMatch (L, "\tjsr\tcomplax")) {
3701 /* We know about this function */
3708 } else if (LineFullMatch (L, "\tjsr\tdecax1")) {
3709 /* We know about this function */
3711 } else if (LineFullMatch (L, "\tjsr\tdecax2")) {
3712 /* We know about this function */
3714 } else if (LineFullMatch (L, "\tjsr\tdecaxy")) {
3715 /* We know about this function */
3717 } else if (LineFullMatch (L, "\tjsr\tdeceaxy")) {
3718 /* We know about this function */
3720 } else if (LineFullMatch (L, "\tjsr\tincax1")) {
3721 /* We know about this function */
3723 } else if (LineFullMatch (L, "\tjsr\tincax2")) {
3724 /* We know about this function */
3726 } else if (LineFullMatch (L, "\tjsr\tinceaxy")) {
3727 /* We know about this function */
3729 } else if (LineFullMatch (L, "\tjsr\tladdeq")) {
3730 /* We know about this function */
3733 } else if (LineFullMatch (L, "\tjsr\tladdeqb")) {
3734 /* We know about this function */
3737 } else if (LineFullMatch (L, "\tjsr\tlbneg")) {
3738 /* We know about this function */
3741 } else if (LineFullMatch (L, "\tjsr\tldai")) {
3742 /* We know about this function */
3745 } else if (LineFullMatch (L, "\tjsr\tldaidx")) {
3746 /* We know about this function */
3748 } else if (LineFullMatch (L, "\tjsr\tldau00sp")) {
3749 /* We know about this function */
3753 } else if (LineFullMatch (L, "\tjsr\tldau0ysp")) {
3754 /* We know about this function */
3758 } else if (LineFullMatch (L, "\tjsr\tldaui")) {
3759 /* We know about this function */
3763 } else if (LineFullMatch (L, "\tjsr\tldaui0sp")) {
3767 } else if (LineFullMatch (L, "\tjsr\tldauidx")) {
3768 /* We know about this function */
3771 } else if (LineFullMatch (L, "\tjsr\tldauiysp")) {
3772 /* We know about this function */
3776 } else if (LineFullMatch (L, "\tjsr\tldax0sp")) {
3777 /* We know about this function */
3780 } else if (LineFullMatch (L, "\tjsr\tldaxi")) {
3781 /* We know about this function */
3784 } else if (LineFullMatch (L, "\tjsr\tldaxidx")) {
3785 /* We know about this function */
3788 } else if (LineFullMatch (L, "\tjsr\tldaxysp")) {
3789 /* We know about this function */
3792 } else if (LineFullMatch (L, "\tjsr\tldeaxi")) {
3793 /* We know about this function */
3796 } else if (LineFullMatch (L, "\tjsr\tldeaxidx")) {
3797 /* We know about this function */
3800 } else if (LineFullMatch (L, "\tjsr\tlsubeq")) {
3801 /* We know about this function */
3804 } else if (LineFullMatch (L, "\tjsr\tlsubeqb")) {
3805 /* We know about this function */
3808 } else if (LineFullMatch (L, "\tjsr\tnegax")) {
3809 /* We know about this function */
3811 } else if (LineFullMatch (L, "\tjsr\tnegeax")) {
3812 /* We know about this function */
3814 } else if (LineFullMatch (L, "\tjsr\tpush0")) {
3815 /* We know about this function */
3819 } else if (LineFullMatch (L, "\tjsr\tpush1")) {
3820 /* We know about this function */
3824 } else if (LineFullMatch (L, "\tjsr\tpush2")) {
3825 /* We know about this function */
3829 } else if (LineFullMatch (L, "\tjsr\tpush3")) {
3830 /* We know about this function */
3834 } else if (LineFullMatch (L, "\tjsr\tpush4")) {
3835 /* We know about this function */
3839 } else if (LineFullMatch (L, "\tjsr\tpush5")) {
3840 /* We know about this function */
3844 } else if (LineFullMatch (L, "\tjsr\tpush6")) {
3845 /* We know about this function */
3849 } else if (LineFullMatch (L, "\tjsr\tpush7")) {
3850 /* We know about this function */
3854 } else if (LineFullMatch (L, "\tjsr\tpusha")) {
3855 /* We know about this function */
3857 } else if (LineFullMatch (L, "\tjsr\tpusha0")) {
3858 /* We know about this function
3859 * If X is already zero, we may call pushax instead and save two
3863 L = ReplaceLine (L, "\tjsr\tpushax");
3867 } else if (LineFullMatch (L, "\tjsr\tpushax")) {
3868 /* We know about this function */
3870 } else if (LineFullMatch (L, "\tjsr\tpushaysp")) {
3871 /* We know about this function */
3874 } else if (LineFullMatch (L, "\tjsr\tpushc0")) {
3875 /* We know about this function */
3878 } else if (LineFullMatch (L, "\tjsr\tpushc1")) {
3879 /* We know about this function */
3882 } else if (LineFullMatch (L, "\tjsr\tpushc2")) {
3883 /* We know about this function */
3886 } else if (LineFullMatch (L, "\tjsr\tpushw")) {
3887 /* We know about this function (calls pushax) */
3890 } else if (LineFullMatch (L, "\tjsr\tpushw0sp")) {
3891 /* We know about this function(calls pushax) */
3894 } else if (LineFullMatch (L, "\tjsr\tpushwidx")) {
3895 /* We know about this function (calls pushax) */
3898 } else if (LineFullMatch (L, "\tjsr\tpushwysp")) {
3899 /* We know about this function (calls pushax) */
3902 } else if (LineFullMatch (L, "\tjsr\tresteax")) {
3903 /* We know about this function */
3905 } else if (LineFullMatch (L, "\tjsr\tsaveeax")) {
3906 /* We know about this function */
3907 /* Changes nothing */
3908 } else if (LineFullMatch (L, "\tjsr\tshrax1")) {
3909 /* We know about this function */
3911 } else if (LineFullMatch (L, "\tjsr\tshrax2")) {
3912 /* We know about this function */
3914 } else if (LineFullMatch (L, "\tjsr\tshrax3")) {
3915 /* We know about this function */
3917 } else if (LineFullMatch (L, "\tjsr\tshreax1")) {
3918 /* We know about this function */
3920 } else if (LineFullMatch (L, "\tjsr\tshreax2")) {
3921 /* We know about this function */
3923 } else if (LineFullMatch (L, "\tjsr\tshreax3")) {
3924 /* We know about this function */
3926 } else if (LineFullMatch (L, "\tjsr\tstaspp")) {
3927 /* We know about this function */
3929 } else if (LineFullMatch (L, "\tjsr\tstaxspp")) {
3930 /* We know about this function */
3932 } else if (LineFullMatch (L, "\tjsr\tstax0sp")) {
3933 /* We know about this function */
3935 } else if (LineFullMatch (L, "\tjsr\tstaxysp")) {
3936 /* We know about this function */
3938 } else if (LineFullMatch (L, "\tjsr\tsubeq0sp")) {
3939 /* We know about this function */
3942 } else if (LineFullMatch (L, "\tjsr\tsubeqysp")) {
3943 /* We know about this function */
3946 } else if (LineFullMatch (L, "\tjsr\ttosadda0")) {
3947 /* We know about this function */
3950 } else if (LineFullMatch (L, "\tjsr\ttosaddax")) {
3951 /* We know about this function */
3954 } else if (LineFullMatch (L, "\tjsr\ttosicmp")) {
3955 /* We know about this function */
3958 } else if (LineFullMatchX (L, Compares) >= 0) {
3961 } else if (LineFullMatchX (L, MakeBool) >= 0) {
3964 } else if (LineMatch (L, "\tjsr\t")) {
3965 /* Subroutine call, forget all register information */
3967 } else if (LineMatch (L, "\tlda\t")) {
3968 if (!RegAUsed (L) && !IsCondJump (NextInstruction (L))) {
3969 /* The value loaded is not used later, remove it */
3971 } else if (LineMatch (L, "\tlda\t(")) {
3972 if (IsXAddrMode (L)) {
3973 /* lda (zp,x) - if Y and X are both zero, replace by
3974 * load indirect y and save one cycle in some cases.
3976 if (X == 0 && Y == 0) {
3978 const char* S = L->Line + 6;
3980 strcpy (Buf, "\tlda\t(");
3988 L = ReplaceLine (L, Buf);
3991 /* In any case invalidate A */
3993 } else if (LineMatch (L, "\tlda\t#$")) {
3994 /* Immidiate load into A */
3995 NewVal = GetHexNum (L->Line + 7);
3997 /* Load has no effect */
3999 } else if (NewVal == X) {
4000 /* Requested value is already in X */
4001 L = ReplaceLine (L, "\ttxa");
4002 } else if (NewVal == Y) {
4003 /* Requested value is already in Y */
4004 L = ReplaceLine (L, "\ttya");
4005 } else if (CPU == CPU_65C02 && A != -1) {
4006 /* Try ina/dea operators of 65C02 */
4007 if (NewVal == ((A - 1) & 0xFF)) {
4008 L = ReplaceLine (L, "\tdea");
4009 } else if (NewVal == ((A + 1) & 0xFF)) {
4010 L = ReplaceLine (L, "\tina");
4013 /* Anyway, the new value is now in A */
4016 /* Memory load into A */
4019 } else if (LineMatch (L, "\tldax\t")) {
4020 /* Memory load into A and X */
4022 } else if (LineMatch (L, "\tldx\t")) {
4023 if (!RegXUsed (L) && !IsCondJump (NextInstruction (L))) {
4024 /* The value loaded is not used later, remove it */
4026 } else if (LineMatch (L, "\tldx\t#$")) {
4027 /* Immidiate load into X */
4028 NewVal = GetHexNum (L->Line + 7);
4030 /* Load has no effect */
4032 } else if (NewVal == A) {
4033 /* Requested value is already in A */
4034 L = ReplaceLine (L, "\ttax");
4035 } else if (X != -1 && NewVal == ((X + 1) & 0xFF)) {
4036 /* Requested value is one more than current contents */
4037 L = ReplaceLine (L, "\tinx");
4038 } else if (X != -1 && NewVal == ((X - 1) & 0xFF)) {
4039 /* Requested value is one less than current contents */
4040 L = ReplaceLine (L, "\tdex");
4042 /* Anyway, the new value is now in X */
4045 /* Memory load into X */
4048 } else if (LineMatch (L, "\tldy\t")) {
4049 if (!RegYUsed (L) && !IsCondJump (NextInstruction (L))) {
4050 /* The value loaded is not used later, remove it */
4052 } else if (LineMatch (L, "\tldy\t#$")) {
4053 /* Immidiate load into Y */
4054 NewVal = GetHexNum (L->Line + 7);
4056 /* Load has no effect */
4058 } else if (NewVal == A) {
4059 /* Requested value is already in A */
4060 L = ReplaceLine (L, "\ttay");
4061 } else if (Y != -1 && NewVal == ((Y + 1) & 0xFF)) {
4062 /* Requested value is one more than current contents */
4063 L = ReplaceLine (L, "\tiny");
4064 } else if (Y != -1 && NewVal == ((Y - 1) & 0xFF)) {
4065 /* Requested value is one less than current contents */
4066 L = ReplaceLine (L, "\tdey");
4068 /* Anyway, the new value is now in Y */
4071 /* Memory load into Y */
4074 } else if (LineFullMatch (L, "\tlsr\ta")) {
4078 } else if (LineMatch (L, "\tora\t#$")) {
4080 A |= GetHexNum (L->Line + 7);
4082 } else if (LineMatch (L, "\tora\t")) {
4084 } else if (LineFullMatch (L, "\tpla")) {
4086 } else if (LineFullMatch (L, "\trol\ta")) {
4088 } else if (LineFullMatch (L, "\tror\ta")) {
4090 } else if (LineFullMatch (L, "\trts")) {
4092 } else if (LineFullMatch (L, "\trti")) {
4094 } else if (LineMatch (L, "\tsbc\t")) {
4095 if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
4096 L->Line[strlen(L->Line)-2] = '\0';
4099 } else if (CPU == CPU_65C02 && LineMatch (L, "\tst")) {
4100 /* Try to replace by stz if possible */
4101 if (A == 0 && LineMatch (L, "\tsta\t")) {
4102 /* Not indirect and not Y allowed */
4103 if (L->Line[5] != '(' && !IsYAddrMode (L)) {
4106 } else if (X == 0 && LineMatch (L, "\tstx\t")) {
4107 /* absolute,y not allowed */
4108 if (!IsYAddrMode (L)) {
4111 } else if (Y == 0 && LineMatch (L, "\tsty\t")) {
4112 /* sty and stz share all addressing modes */
4115 } else if (LineFullMatch (L, "\ttax")) {
4116 if (A != -1 && X == A) {
4117 /* Load has no effect */
4122 } else if (LineFullMatch (L, "\ttay")) {
4123 if (A != -1 && Y == A) {
4124 /* Load has no effect */
4129 } else if (LineFullMatch (L, "\ttsx")) {
4131 } else if (LineFullMatch (L, "\ttxa")) {
4132 if (X != -1 && A == X) {
4133 /* Load has no effect */
4138 } else if (LineFullMatch (L, "\ttya")) {
4139 if (Y != -1 && A == Y) {
4140 /* Load has no effect */
4147 /* Set to next line, handle deletions */
4148 L2 = NextCodeSegLine (L);
4156 /* Skip the label */
4157 L = NextCodeSegLine (L);
4164 static void OptBlocks (void)
4165 /* Optimize the register contents inside basic blocks */
4167 Line* L = FirstCode;
4169 L = OptOneBlock (L);
4175 static void OptJumps (void)
4176 /* Optimize jumps */
4178 static const char* Jumps [] = {
4188 Line* L = FirstCode;
4190 int I = LineMatchX (L, Jumps);
4192 Line* Target = GetTargetLine (L->Line+5);
4193 if (Target->Index > L->Index) {
4194 /* This is a forward jump. Backward jumps are handled
4195 * automagically by the assembler.
4197 unsigned Distance = GetJumpDistance (L, Target);
4198 if (Distance < 123) { /* Safety */
4199 L->Line [1] = 'b'; /* Make a short branch */
4200 L->Size = 2; /* Set new size */
4204 L = NextCodeLine (L);
4207 /* Special treatment for jumps on the 65C02 */
4208 if (CPU == CPU_65C02) {
4210 Line* L = FirstCode;
4212 if (LineMatch (L, "\tjmp\tL")) {
4213 Line* Target = GetTargetLine (L->Line+5);
4214 unsigned Distance = GetJumpDistance (L, Target);
4215 if (Distance < 123) { /* Safety */
4216 L->Line [1] = 'b'; /* Make a short branch */
4219 L->Size = 2; /* Set new size */
4222 L = NextCodeLine (L);
4230 static void OptRTS (void)
4231 /* Change sequences of jsr XXX/rts to jmp XXX */
4233 Line* L = FirstCode;
4235 if (LineMatch (L, "\tjsr\t")) {
4236 /* This is a jsr, get the next instruction */
4237 Line* L2 = NextCodeLine (L);
4238 if (L2 && LineFullMatch (L2, "\trts")) {
4239 /* We found a sequence */
4245 /* Try the next line */
4246 L = NextCodeLine (L);
4252 /*****************************************************************************/
4254 /*****************************************************************************/
4258 static void OptOnePass (unsigned long Flag, void (*F) (void))
4259 /* Call one optimizer pass if enabled */
4261 if ((OptDisable & Flag) == 0) {
4263 } else if (Verbose || Debug) {
4264 printf ("Optimizer pass %04lX skipped\n", Flag);
4270 void OptDoOpt (void)
4271 /* Run the optimizer over the collected stuff */
4273 /* Find and remember the first line of code */
4276 /* Mark all lines inside the code segment */
4279 /* Create a list of all local labels for fast access */
4282 /* Ok, now start the real optimizations */
4284 /* Optimize compares - first step */
4285 OptOnePass (0x0001, OptCompares1);
4287 /* Remove dead jumps */
4288 OptOnePass (0x0002, OptDeadJumps);
4290 /* Remove unnecessary loads */
4291 OptOnePass (0x0004, OptLoads);
4293 /* Remove unnecessary register loads */
4294 OptOnePass (0x0008, OptRegLoads);
4296 /* Optimize stores through pointers */
4297 OptOnePass (0x0010, OptPtrOps);
4299 /* Optimize use of register variables */
4300 OptOnePass (0x0020, OptRegVars);
4302 /* Remove jump cascades - must be used before OptNeg */
4303 OptOnePass (0x0040, OptDoubleJumps);
4305 /* Remove unnecessary boolean negates */
4306 OptOnePass (0x0080, OptNeg);
4308 /* Replace jumps to an RTS by an RTS */
4309 OptOnePass (0x0100, OptJumpRTS);
4311 /* Optimize boolean transforms */
4312 OptOnePass (0x0200, OptBoolTransforms);
4314 /* Optimize compares */
4315 OptOnePass (0x0400, OptCompares2);
4317 /* Remove unnecessary tests */
4318 OptOnePass (0x0800, OptTests);
4320 /* Optimize several triples */
4321 OptOnePass (0x1000, OptTriples);
4323 /* Optimize basic blocks */
4324 OptOnePass (0x2000, OptBlocks);
4326 /* Remove unnecessary register loads (another pass) */
4327 OptOnePass (0x0008, OptRegLoads);
4329 /* Optimize jumps */
4330 OptOnePass (0x4000, OptJumps);
4332 /* Optimize jsr/rts sequences */
4333 OptOnePass (0x8000, OptRTS);