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 /*****************************************************************************/
52 /*****************************************************************************/
54 /*****************************************************************************/
58 /* Bitset of flags that switch the different optimizer passes */
59 unsigned long OptDisable = 0;
63 /* Bitmapped flags for the Flags field in the Line struct */
64 #define OF_CODE 0x0001 /* This line is in a code segment */
66 /* Pointer to first code line */
67 static Line* FirstCode;
70 static Line** Labels = 0; /* Pointers to label lines */
71 static unsigned LabelCount = 0; /* Count of local labels found */
73 /* A collection of lines */
74 typedef struct LineColl_ LineColl;
76 unsigned Count; /* Count of lines in the collection */
77 unsigned Max; /* Maximum count of lines */
78 Line* Lines[1]; /* Lines, dynamically allocated */
83 /* Calculate the element count of a table */
84 #define COUNT(T) (sizeof (T) / sizeof (T [0]))
86 /* Macro to increment and decrement register contents if they're valid */
87 #define INC(reg,val) if ((reg) >= 0) (reg) = ((reg) + val) & 0xFF
88 #define DEC(reg,val) if ((reg) >= 0) (reg) = ((reg) - val) & 0xFF
90 /* Defines for the conditions in a compare */
102 /* Defines for registers */
103 #define REG_NONE 0x00
107 #define REG_AX (REG_A | REG_X)
108 #define REG_ALL (REG_A | REG_X | REG_Y)
110 /* Description of the commands */
111 static const struct {
112 const char* Insn; /* Instruction */
113 unsigned char FullMatch; /* Match full instuction? */
114 unsigned char Use; /* Registers used */
115 unsigned char Load; /* Registers loaded */
117 { "\tadc\t", 0, REG_A, REG_NONE },
118 { "\tand\t", 0, REG_A, REG_NONE },
119 { "\tasl\ta", 1, REG_A, REG_NONE },
120 { "\tasl\t", 0, REG_NONE, REG_NONE },
121 { "\tclc", 1, REG_NONE, REG_NONE },
122 { "\tcld", 1, REG_NONE, REG_NONE },
123 { "\tcli", 1, REG_NONE, REG_NONE },
124 { "\tcmp\t", 0, REG_A, REG_NONE },
125 { "\tcpx\t", 0, REG_X, REG_NONE },
126 { "\tcpy\t", 0, REG_Y, REG_NONE },
127 { "\tdea", 1, REG_A, REG_NONE },
128 { "\tdec\ta", 1, REG_A, REG_NONE },
129 { "\tdec\t", 0, REG_NONE, REG_NONE },
130 { "\tdex", 1, REG_X, REG_NONE },
131 { "\tdey", 1, REG_Y, REG_NONE },
132 { "\teor\t", 0, REG_A, REG_NONE },
133 { "\tina", 1, REG_A, REG_NONE },
134 { "\tinc\ta", 1, REG_A, REG_NONE },
135 { "\tinc\t", 0, REG_NONE, REG_NONE },
136 { "\tinx", 1, REG_X, REG_NONE },
137 { "\tiny", 1, REG_Y, REG_NONE },
138 { "\tjsr\tbool", 0, REG_NONE, REG_AX },
139 { "\tjsr\tdecaxy", 1, REG_ALL, REG_AX },
140 { "\tjsr\tdecax", 0, REG_AX, REG_AX },
141 { "\tjsr\tldax0sp", 1, REG_Y, REG_AX },
142 { "\tjsr\tldaxysp", 1, REG_Y, REG_AX },
143 { "\tjsr\tpusha", 1, REG_A, REG_Y },
144 { "\tjsr\tpusha0", 1, REG_A, REG_X | REG_Y },
145 { "\tjsr\tpushax", 1, REG_AX, REG_Y },
146 { "\tjsr\tpushw0sp", 1, REG_NONE, REG_ALL },
147 { "\tjsr\tpushwysp", 1, REG_Y, REG_ALL },
148 { "\tjsr\ttosicmp", 1, REG_AX, REG_ALL },
149 { "\tlda\t", 0, REG_NONE, REG_A },
150 { "\tldax\t", 0, REG_NONE, REG_AX },
151 { "\tldx\t", 0, REG_NONE, REG_X },
152 { "\tldy\t", 0, REG_NONE, REG_Y },
153 { "\tlsr\ta", 1, REG_A, REG_NONE },
154 { "\tlsr\t", 0, REG_NONE, REG_NONE },
155 { "\tnop", 1, REG_NONE, REG_NONE },
156 { "\tora\t", 0, REG_A, REG_NONE },
157 { "\tpha", 1, REG_A, REG_NONE },
158 { "\tphp", 1, REG_NONE, REG_NONE },
159 { "\tpla", 1, REG_NONE, REG_A },
160 { "\tplp", 1, REG_NONE, REG_NONE },
161 { "\trol\ta", 1, REG_A, REG_A },
162 { "\trol\t", 0, REG_NONE, REG_NONE },
163 { "\tror\ta", 1, REG_A, REG_A },
164 { "\tror\t", 0, REG_NONE, REG_NONE },
165 { "\tsbc\t", 0, REG_A, REG_NONE },
166 { "\tsec", 1, REG_NONE, REG_NONE },
167 { "\tsed", 1, REG_NONE, REG_NONE },
168 { "\tsei", 1, REG_NONE, REG_NONE },
169 { "\tsta\t", 0, REG_A, REG_NONE },
170 { "\tstx\t", 0, REG_X, REG_NONE },
171 { "\tsty\t", 0, REG_Y, REG_NONE },
172 { "\tstz\t", 0, REG_NONE, REG_NONE },
173 { "\ttax", 1, REG_A, REG_X },
174 { "\ttay", 1, REG_A, REG_Y },
175 { "\ttsx", 1, REG_NONE, REG_X },
176 { "\ttxa", 1, REG_X, REG_A },
177 { "\ttya", 1, REG_Y, REG_A },
182 /* Table with the compare suffixes */
183 static const char CmpSuffixTab [][4] = {
184 "eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule"
187 /* Table used to invert a condition, indexed by condition */
188 static const unsigned char CmpInvertTab [] = {
190 CMP_LE, CMP_LT, CMP_GE, CMP_GT,
191 CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT
194 /* Table to show which compares are signed (use the N flag) */
195 static const char CmpSignedTab [] = {
196 0, 0, 1, 1, 1, 1, 0, 0, 0, 0
201 /* Lists of branches */
202 static const char* ShortBranches [] = {
213 static const char* LongBranches [] = {
227 /*****************************************************************************/
229 /*****************************************************************************/
233 static unsigned EstimateSize (Line* L);
234 /* Estimate the size of an instruction */
236 static int IsLocalLabel (const Line* L);
237 /* Return true if the line is a local label line */
239 static unsigned GetLabelNum (const char* L);
240 /* Return the label number of a label line */
242 static unsigned RVUInt1 (Line* L, LineColl* LC, unsigned Used, unsigned Unused);
243 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
247 /*****************************************************************************/
249 /*****************************************************************************/
253 static Line* NewLineAfter (Line* LineBefore, const char* Format, ...)
254 /* Create a new line, insert it after L and return it. The new line is marked
260 /* Format the new line and add it */
262 va_start (ap, Format);
263 L = NewCodeLineAfter (LineBefore, Format, ap);
266 /* Make the line a code line */
269 /* Estimate the code size */
270 L->Size = EstimateSize (L);
272 /* Return the new line */
278 static Line* NewLabelAfter (Line* L, unsigned Label)
279 /* Add a new line with a definition of a local label after the line L */
283 /* Create the label */
284 sprintf (Buf, "L%04X:", Label);
286 /* Create a new line */
287 L = NewLineAfter (L, Buf);
289 /* Insert this label into the label list */
292 /* Return the new line */
298 static void FreeLine (Line* L)
299 /* Remove a line from the list and free it */
301 /* If this is a label line, remove it from the label list */
302 if (IsLocalLabel (L)) {
303 Labels [GetLabelNum (L->Line)] = 0;
306 /* Unlink the line */
312 static Line* ReplaceLine (Line* L, const char* Format, ...)
313 /* Replace one line by another */
318 /* Format the new line */
320 va_start (ap, Format);
321 vsprintf (S, Format, ap);
324 /* Get the length of the new line */
327 /* We can copy the line if the old line has space enough */
330 /* Just copy the new line, but don't update the length */
331 memcpy (L->Line, S, Len);
332 L->Line [Len] = '\0';
336 /* We must allocate new space */
337 Line* NewLine = xmalloc (sizeof (Line) + Len);
339 /* Set the values in the new struct */
340 NewLine->Flags = L->Flags;
341 NewLine->Index = L->Index;
342 NewLine->Size = L->Size; /* Hmm ... */
344 memcpy (NewLine->Line, S, Len + 1);
346 /* Replace the old struct in the list */
347 NewLine->Next = L->Next;
349 NewLine->Next->Prev = NewLine;
354 NewLine->Prev = L->Prev;
356 NewLine->Prev->Next = NewLine;
362 /* Free the old struct */
367 /* Estimate the new size */
368 if (L->Flags & OF_CODE) {
369 L->Size = EstimateSize (L);
372 /* Return the line */
378 static Line* PrevCodeLine (Line* L)
379 /* Return the previous line containing code */
383 if (L->Flags & OF_CODE && L->Line [0] != '+') {
393 static Line* NextCodeSegLine (Line* L)
394 /* Return the next line in the code segment */
398 if (L->Flags & OF_CODE) {
408 static Line* NextCodeLine (Line* L)
409 /* Return the next line containing code */
413 if ((L->Flags & OF_CODE) != 0 && L->Line [0] != '+') {
423 static Line* NextInstruction (Line* L)
424 /* Return the next line containing code, ignoring labels. */
427 L = NextCodeLine (L);
428 } while (L && (L->Line[0] == '+' || IsLocalLabel(L)));
434 static void FreeLines (Line* Start, Line* End)
435 /* Delete all lines from Start to End, both inclusive */
440 Start = NextCodeSegLine (Start);
447 /*****************************************************************************/
448 /* Line Collections */
449 /*****************************************************************************/
453 static LineColl* NewLineColl (unsigned Size)
454 /* Create a new line collection and return it */
456 /* Allocate memory */
457 LineColl* LC = xmalloc (sizeof (LineColl) + sizeof (Line) * (Size-1));
459 /* Initialize members */
463 /* Return the new collection */
469 static void FreeLineColl (LineColl* LC)
470 /* Delete a line collection */
477 static int LCAddLine (LineColl* LC, Line* L)
478 /* Add a line. Return 0 if no space available, return 1 otherwise */
480 /* Check if there is enough space available */
481 if (LC->Count >= LC->Max) {
482 /* No room available */
487 LC->Lines [LC->Count++] = L;
495 static int LCHasLine (LineColl* LC, Line* L)
496 /* Check if the given line is in the collection */
499 for (I = 0; I < LC->Count; ++I) {
500 if (LC->Lines[I] == L) {
509 /*****************************************************************************/
510 /* Test a line for several things */
511 /*****************************************************************************/
515 static int IsLocalLabel (const Line* L)
516 /* Return true if the line is a local label line */
518 return (L->Line [0] == 'L' && isxdigit (L->Line [1]));
523 static int IsLabel (const Line* L)
524 /* Return true if the line is a label line */
526 return (L->Line [0] == 'L' && isxdigit (L->Line [1])) ||
527 (L->Line [0] == '_');;
532 static int IsHintLine (const Line* L)
533 /* Return true if the line contains an optimizer hint */
535 return L->Line [0] == '+';
540 static int IsSegHint (const Line* L)
541 /* Return true if the given line contains a segment hint */
543 return (L->Line [0] == '+' && strncmp (L->Line + 1, "seg:", 4) == 0);
548 static int IsHint (const Line* L, const char* Hint)
549 /* Check if the line contains a given hint */
551 return (L->Line [0] == '+' && strcmp (L->Line + 1, Hint) == 0);
556 static int IsCondJump (Line* L)
557 /* Return true if the line contains a conditional jump */
559 return (L->Line [0] == '\t' &&
560 (strncmp (L->Line + 1, "beq\t", 4) == 0 ||
561 strncmp (L->Line + 1, "bne\t", 4) == 0 ||
562 strncmp (L->Line + 1, "jeq\t", 4) == 0 ||
563 strncmp (L->Line + 1, "jne\t", 4) == 0));
568 static int IsXAddrMode (Line* L)
569 /* Return true if the given line does use the X register */
571 unsigned Len = strlen (L->Line);
572 return (strcmp (L->Line + Len - 3, ",x)") == 0 ||
573 strcmp (L->Line + Len - 2, ",x") == 0);
578 static int NoXAddrMode (Line* L)
579 /* Return true if the given line does use the X register */
581 return !IsXAddrMode (L);
586 static int IsYAddrMode (Line* L)
587 /* Return true if the given line does use the Y register */
589 unsigned Len = strlen (L->Line);
590 return (strcmp (L->Line + Len - 2, ",y") == 0);
595 static Line* FindHint (Line* L, const char* Hint)
596 /* Search for a line with the given hint */
599 if (IsHint (L, Hint)) {
609 static unsigned GetHexNum (const char* S)
610 /* Get a hex number from a string */
614 while (isxdigit (S [I])) {
615 int C = (unsigned char) (S [I++]);
621 Val = (Val << 4) + C;
628 static unsigned GetLabelNum (const char* L)
629 /* Return the label number of a label line */
632 return GetHexNum (L+1);
637 static Line* GetTargetLine (const char* L)
638 /* Get the line with the target label of a jump. L must be a pointer to the
639 * string containing the label number.
644 /* Get the label number of the target */
645 unsigned Label = GetLabelNum (L);
646 CHECK (Label < LabelCount);
648 /* Get the line with this label */
649 Target = Labels [Label];
650 CHECK (Target != 0 && (Target->Flags & OF_CODE) != 0);
658 static unsigned GetJumpDistance (Line* L, Line* Target)
659 /* Get the distance between both lines */
661 unsigned Distance = 0;
664 if (Target->Index > L->Index) {
665 /* This is a forward jump. */
667 L = NextCodeLine (L);
669 } while (L != Target);
671 /* This is a backward jump */
673 L = PrevCodeLine (L);
675 } while (L != Target);
679 /* Return the calculated distance */
685 static int LineMatch (const Line* L, const char* Start)
686 /* Check if the start of the line matches Start */
688 return strncmp (L->Line, Start, strlen (Start)) == 0;
693 static int LineFullMatch (const Line* L, const char* Start)
694 /* Check if the matches Start */
696 return strcmp (L->Line, Start) == 0;
701 static int LineMatchX (const Line* L, const char** Start)
702 /* Check the start of the line against a list of patterns. Return the
703 * number of the pattern that matched, or -1 in case of no match.
708 if (LineMatch (L, *Start)) {
721 static int LineFullMatchX (const Line* L, const char** Start)
722 /* Check the the line against a list of patterns. Return the
723 * number of the pattern that matched, or -1 in case of no match.
728 if (LineFullMatch (L, *Start)) {
741 static int IsLoadAX (Line* L1, Line* L2)
742 /* Check if the both lines load a static variable into ax. That is, both lines
748 return LineMatch (L1, "\tlda\t") &&
749 LineMatch (L2, "\tldx\t") &&
750 strncmp (L1->Line+5, L2->Line+5, strlen (L1->Line+5)) == 0 &&
751 strcmp (L2->Line+strlen(L1->Line), "+1") == 0;
756 /*****************************************************************************/
757 /* Initial optimizer setup */
758 /*****************************************************************************/
762 static void FindCodeStart (void)
763 /* Find and remember the first line of actual code */
765 Line* L = FindHint (FirstLine, "end_of_preamble");
766 FirstCode = L? L->Next : 0;
771 static unsigned EstimateDataSize (Line* L, unsigned Chunk)
772 /* Estimate the size of a .byte, .word or .dword command */
774 unsigned Size = Chunk;
776 while ((S = strchr (S, ',')) != 0) {
785 static unsigned EstimateSize (Line* L)
786 /* Estimate the size of an instruction */
788 static const char* OneByteCmds [] = {
804 if (L->Line [0] != '\t') {
807 if (LineMatch (L, "\tldax\t")) {
808 /* Immidiate load of both, A and X */
811 if (LineMatch (L, "\tld")) {
812 OpStart = L->Line [5];
813 return (OpStart == '#' || OpStart == '(')? 2 : 3;
815 if (LineMatch (L, "\tst")) {
816 OpStart = L->Line [5];
817 return (OpStart == '(')? 2 : 3;
819 if (LineMatch (L, "\t.byte\t")) {
820 return EstimateDataSize (L, 1);
822 if (LineMatch (L, "\t.word\t")) {
823 return EstimateDataSize (L, 2);
825 if (LineMatch (L, "\t.dword\t")) {
826 return EstimateDataSize (L, 4);
828 if (LineMatchX (L, ShortBranches) >= 0) {
831 if (LineMatchX (L, LongBranches) >= 0) {
834 if (LineMatchX (L, OneByteCmds) >= 0) {
842 static void MarkCodeLines (void)
843 /* Mark all lines that are inside a code segment */
849 InCode = IsHint (L, "seg:code");
850 } else if (InCode && L->Line[0] != '\0') {
852 L->Size = EstimateSize (L);
860 static void CreateLabelList (void)
861 /* Create a list with pointers to local labels */
867 /* Get the next label number. This is also the current label count.
868 * Make some room for more labels when optimizing code.
870 LabelCount = GetLabel () + 100;
872 /* Allocate memory for the array and clear it */
873 Labels = xmalloc (LabelCount * sizeof (Line*));
874 for (I = 0; I < LabelCount; ++I) {
878 /* Walk through the code and insert all label lines */
881 if (IsLocalLabel (L)) {
882 unsigned LabelNum = GetLabelNum (L->Line);
883 CHECK (LabelNum < LabelCount);
884 Labels [LabelNum] = L;
892 static unsigned AllocLabel (void)
893 /* Get a new label. The current code does not realloc the label list, so there
894 * must be room enough in the current list.
899 /* Search for a free slot, start at 1, since 0 is "no label" */
900 for (I = 1; I < LabelCount; ++I) {
901 if (Labels[I] == 0) {
902 /* Found a free slot */
907 /* No label space available */
908 Internal ("Out of label space in the optimizer");
916 /*****************************************************************************/
917 /* Helper functions */
918 /*****************************************************************************/
922 static int GetNextCodeLines (Line* L, Line** Lines, unsigned Count)
923 /* Get a number of code lines ignoring hints and other stuff. The function
924 * returns 1 if we got the lines and 0 if we are at the end of the code
925 * segment or if we hit a label.
930 /* Get the next valid line */
932 L = NextCodeLine (L);
933 } while (L && IsHintLine (L));
935 /* Did we get one? */
936 if (L == 0 || IsLabel (L)) {
941 /* Remember the line */
951 static int FindCond (const char* Suffix)
952 /* Map a condition suffix to a code. Return the code or -1 on failure */
957 for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) {
958 if (strncmp (Suffix, CmpSuffixTab [I], strlen (CmpSuffixTab[I])) == 0) {
970 static int CheckAndGetIntCmp (const Line* JSR, const Line* JMP)
971 /* Helper function to check for a compare subroutine call followed by a
972 * conditional branch. Will return the condition found, or -1 if no
973 * or invalid condition.
980 /* Extract the condition from the function name. */
981 if ((Cond [0] = JSR->Line [8]) == 'u') {
982 Cond [1] = JSR->Line [9];
983 Cond [2] = JSR->Line [10];
985 Tail = JSR->Line + 11;
987 Cond [1] = JSR->Line [9];
989 Tail = JSR->Line + 10;
992 /* Check if this is indeed an integer function */
993 if (strcmp (Tail, "ax") != 0) {
998 /* Get the condition code */
1005 /* Invert the code if we jump on condition not met. */
1006 if (JMP->Line [2] == 'e' && JMP->Line [3] == 'q') {
1007 /* Jumps if condition false, invert condition */
1008 C = CmpInvertTab [C];
1011 /* Return the condition code */
1017 static int TosCmpFunc (Line* L)
1018 /* Check if this is a call to one of the TOS compare functions (tosgtax).
1019 * Return the condition code or -1 if not found.
1022 if (LineMatch (L, "\tjsr\ttos") &&
1023 strcmp (L->Line+strlen(L->Line)-2, "ax") == 0) {
1025 /* Ok, found. Get the condition. */
1026 return FindCond (L->Line+8);
1037 static int IsUnsignedCmp (int Code)
1038 /* Check if this is an unsigned compare */
1041 return CmpSignedTab [Code] == 0;
1046 static void InvertZJump (Line* L)
1047 /* Invert a jeq/jne jump */
1049 if (L->Line [2] == 'n' && L->Line [3] == 'e') {
1050 /* This was a bne/jne */
1054 /* This was (hopefully) a beq/jeq */
1062 static int FindCmd (Line* L)
1066 /* Search for the known patterns */
1067 for (I = 0; I < COUNT(CmdDesc); ++I) {
1068 if (CmdDesc[I].FullMatch) {
1069 if (LineFullMatch (L, CmdDesc[I].Insn)) {
1074 if (LineMatch (L, CmdDesc[I].Insn)) {
1086 static unsigned RVUInt2 (Line* L,
1087 LineColl* LC, /* To remember visited lines */
1088 unsigned Used, /* Definitely used registers */
1089 unsigned Unused) /* Definitely unused registers */
1090 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
1094 /* Check the following instructions. We classifiy them into primary
1095 * loads (register value not used), neutral (check next instruction),
1096 * and unknown (assume register was used).
1102 /* Get the next line and follow jumps */
1105 /* Handle jumps to local labels (continue there) */
1106 if (LineMatch (L, "\tjmp\tL")) {
1107 /* Get the target of the jump */
1108 L = GetTargetLine (L->Line+5);
1111 /* Get the next instruction line */
1112 L = NextInstruction (L);
1114 /* Bail out if we're done */
1115 if (L == 0 || IsLabel (L)) {
1116 /* Something is wrong */
1120 /* Check if we had this line already. If so, bail out, if not,
1121 * add it to the list of known lines.
1123 if (LCHasLine (LC, L) || !LCAddLine (LC, L)) {
1127 } while (LineMatch (L, "\tjmp\tL"));
1129 /* Special handling for branches */
1130 if (LineMatchX (L, ShortBranches) >= 0 ||
1131 LineMatchX (L, LongBranches) >= 0) {
1132 const char* Target = L->Line+5;
1133 if (Target[0] == 'L') {
1134 /* Jump to local label. Check the register usage starting at
1135 * the branch target and at the code following the branch.
1136 * All registers that are unused in both execution flows are
1137 * returned as unused.
1140 U2 = RVUInt1 (GetTargetLine (Target), LC, Used, Unused);
1141 U1 = RVUInt1 (L, LC, Used, Unused);
1142 return U1 | U2; /* Used in any of the branches */
1146 /* Search for the instruction in this line */
1149 /* If we don't find it, assume all other registers are */
1154 /* Evaluate the use flags, check for addressing modes */
1156 if (IsXAddrMode (L)) {
1158 } else if (IsYAddrMode (L)) {
1162 /* Remove registers that were already new loaded */
1165 /* Remember the remaining registers */
1169 /* Evaluate the load flags */
1170 R = CmdDesc[I].Load;
1172 /* Remove registers that were already used */
1175 /* Remember the remaining registers */
1179 /* If we know about all registers, bail out */
1180 if ((Used | Unused) == REG_ALL) {
1186 /* Return to the caller the complement of all unused registers */
1187 return ~Unused & REG_ALL;
1192 static unsigned RVUInt1 (Line* L,
1193 LineColl* LC, /* To remember visited lines */
1194 unsigned Used, /* Definitely used registers */
1195 unsigned Unused) /* Definitely unused registers */
1196 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
1198 /* Remember the current count of the line collection */
1199 unsigned Count = LC->Count;
1201 /* Call the worker routine */
1202 unsigned R = RVUInt2 (L, LC, Used, Unused);
1204 /* Restore the old count */
1207 /* Return the result */
1213 static unsigned RegValUsed (Line* Start)
1214 /* Check the next instructions after the one in L for register usage. If
1215 * a register is used as an index, or in a store or other instruction, it
1216 * is assumed to be used. If a register is loaded with a value, before it
1217 * was used by one of the actions described above, it is assumed unused.
1218 * If the end of the lookahead is reached, all registers that are uncertain
1219 * are marked as used.
1220 * The result of the search is returned.
1225 /* Create a new line collection and enter the start line */
1226 LineColl* LC = NewLineColl (256);
1227 LCAddLine (LC, Start);
1229 /* Call the recursive subfunction */
1230 R = RVUInt1 (Start, LC, REG_NONE, REG_NONE);
1232 /* Delete the line collection */
1235 /* Return the registers used */
1241 static int RegAUsed (Line* Start)
1242 /* Check if the value in A is used. */
1244 return (RegValUsed (Start) & REG_A) != 0;
1249 static int RegXUsed (Line* Start)
1250 /* Check if the value in X is used. */
1252 return (RegValUsed (Start) & REG_X) != 0;
1257 static int RegYUsed (Line* Start)
1258 /* Check if the value in Y is used. */
1260 return (RegValUsed (Start) & REG_Y) != 0;
1265 /*****************************************************************************/
1266 /* Real optimizer routines */
1267 /*****************************************************************************/
1271 static void OptCompares1 (void)
1272 /* Try to optimize the integer compare subroutines. */
1274 Line* L2[10]; /* Line lookahead */
1275 int Cond; /* Condition to evaluate */
1276 unsigned Label; /* Local label number */
1277 unsigned Offs; /* Stack offset */
1278 Line* DelStart; /* First line to delete */
1280 Line* L = FirstCode;
1283 /* Search for compares of local byte sized variables. This looks
1295 * Replace it by a direct compare:
1303 if (LineMatch (L, "\tldy\t#$") &&
1304 GetNextCodeLines (L, L2, 7) &&
1305 LineFullMatch (L2[0], "\tldx\t#$00") &&
1306 LineFullMatch (L2[1], "\tlda\t(sp),y") &&
1307 LineFullMatch (L2[2], "\tjsr\tpushax") &&
1308 LineMatch (L2[3], "\tldy\t#$") &&
1309 LineFullMatch (L2[4], "\tldx\t#$00") &&
1310 LineFullMatch (L2[5], "\tlda\t(sp),y") &&
1311 (Cond = TosCmpFunc (L2[6])) >= 0) {
1313 /* Get the stack offset and correct it, since we will remove
1316 Offs = GetHexNum (L2[3]->Line+7) - 2;
1319 L = NewLineAfter (L, "\tlda\t(sp),y");
1320 L = NewLineAfter (L, "\tldy\t#$%02X", Offs);
1321 L = NewLineAfter (L, "\tcmp\t(sp),y");
1322 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1324 /* Remove the old cruft */
1325 FreeLines (L2[0], L2[6]);
1328 /* Compares of byte sized global variables */
1329 else if (LineFullMatch (L, "\tldx\t#$00") &&
1330 GetNextCodeLines (L, L2, 5) &&
1331 LineMatch (L2[0], "\tlda\t") &&
1332 LineFullMatch (L2[1], "\tjsr\tpushax") &&
1333 LineFullMatch (L2[2], "\tldx\t#$00") &&
1334 LineMatch (L2[3], "\tlda\t") &&
1335 (Cond = TosCmpFunc (L2[4])) >= 0) {
1338 if (IsXAddrMode (L2[0])) {
1339 /* The load is X indirect, so we may not remove the load
1340 * of the X register.
1345 L = ReplaceLine (L, L2[0]->Line);
1348 L = NewLineAfter (L, "\tcmp\t%s", L2[3]->Line+5);
1349 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1351 /* Remove the old cruft */
1352 FreeLines (DelStart, L2[4]);
1356 /* Byte sized local to global */
1357 else if (LineMatch (L, "\tldy\t#$") &&
1358 GetNextCodeLines (L, L2, 6) &&
1359 LineFullMatch (L2[0], "\tldx\t#$00") &&
1360 LineFullMatch (L2[1], "\tlda\t(sp),y") &&
1361 LineFullMatch (L2[2], "\tjsr\tpushax") &&
1362 LineFullMatch (L2[3], "\tldx\t#$00") &&
1363 LineMatch (L2[4], "\tlda\t") &&
1364 (Cond = TosCmpFunc (L2[5])) >= 0) {
1367 L = NewLineAfter (L, L2[1]->Line);
1368 L = NewLineAfter (L, "\tcmp\t%s", L2[4]->Line+5);
1369 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1371 /* Remove the old cruft */
1372 FreeLines (L2[0], L2[5]);
1376 /* Byte sized global to local */
1377 else if (LineFullMatch (L, "\tldx\t#$00") &&
1378 GetNextCodeLines (L, L2, 6) &&
1379 LineMatch (L2[0], "\tlda\t") &&
1380 LineFullMatch (L2[1], "\tjsr\tpushax") &&
1381 LineMatch (L2[2], "\tldy\t#$") &&
1382 LineFullMatch (L2[3], "\tldx\t#$00") &&
1383 LineFullMatch (L2[4], "\tlda\t(sp),y") &&
1384 (Cond = TosCmpFunc (L2[5])) >= 0) {
1386 /* Get the stack offset and correct it, since we will remove
1389 Offs = GetHexNum (L2[2]->Line+7) - 2;
1392 if (IsXAddrMode (L2[0])) {
1393 /* The load is X indirect, so we may not remove the load
1394 * of the X register.
1399 L = ReplaceLine (L, L2[0]->Line);
1402 L = NewLineAfter (L, "\tldy\t#$%02X", Offs);
1403 L = NewLineAfter (L, "\tcmp\t(sp),y");
1404 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1406 /* Remove the old cruft */
1407 FreeLines (DelStart, L2[5]);
1411 /* Search for unsigned compares against global variables. This looks
1419 * Replace that by a direct compare:
1427 else if (LineFullMatch (L, "\tjsr\tpushax") &&
1428 GetNextCodeLines (L, L2, 3) &&
1429 IsLoadAX (L2[0], L2[1]) &&
1430 (Cond = TosCmpFunc (L2[2])) >= 0 &&
1431 IsUnsignedCmp (Cond)) {
1433 /* Get a free label number */
1434 Label = AllocLabel ();
1436 /* Replace the code */
1437 L = ReplaceLine (L, "\tcpx\t%s", L2[1]->Line+5);
1438 L = NewLineAfter (L, "\tbne\tL%04X", Label);
1439 L = NewLineAfter (L, "\tcmp\t%s", L2[0]->Line+5);
1440 L = NewLabelAfter(L, Label);
1441 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1443 /* Remove the old code */
1444 FreeLines (L2[0], L2[2]);
1448 L = NextCodeLine (L);
1454 static void OptDeadJumps (void)
1455 /* Remove jumps to the following instruction */
1457 static const char* Jumps [] = {
1466 Line* L = FirstCode;
1469 /* Get a pointer to the next instruction line */
1470 Line* NextLine = NextInstruction (L);
1472 /* Is this line a jump? */
1473 int I = LineMatchX (L, Jumps);
1475 /* Yes. Get the target label, skip labels */
1476 Line* Target = NextInstruction (GetTargetLine (L->Line+5));
1478 /* If the target label is the next line, remove the jump */
1479 if (Target == NextLine) {
1484 /* Go to the next line */
1491 static void OptLoads (void)
1492 /* Remove unnecessary loads of values */
1496 Line* L = FirstCode;
1517 * This change will cost 3 cycles (one additional jump inside the
1518 * subroutine), but it saves a lot of code (6 bytes per occurrence),
1519 * so we will accept the overhead. It may even be possible to rewrite
1520 * the library routine to get rid of the additional overhead.
1522 if (LineMatch (L, "\tldy\t#$") &&
1523 GetNextCodeLines (L, L2, 5) &&
1524 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
1525 LineFullMatch (L2 [1], "\ttax") &&
1526 LineFullMatch (L2 [2], "\tdey") &&
1527 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
1528 LineFullMatch (L2 [4], "\tjsr\tpushax")) {
1530 /* Found - replace it */
1531 if (LineFullMatch (L, "\tldy\t#$01")) {
1532 /* Word at offset zero */
1534 L = ReplaceLine (L2 [4], "\tjsr\tpushw0sp");
1536 ReplaceLine (L2 [4], "\tjsr\tpushwysp");
1539 /* Delete the remaining lines */
1540 FreeLines (L2 [0], L2 [3]);
1563 * This change will cost 2 cycles, but it saves a lot of code (6 bytes
1564 * per occurrence), so we will accept the overhead. It may even be
1565 * possible to rewrite the library routine to get rid of the additional
1568 if (LineMatch (L, "\tldy\t#$") &&
1569 GetNextCodeLines (L, L2, 6) &&
1570 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
1571 LineFullMatch (L2 [1], "\ttax") &&
1572 LineFullMatch (L2 [2], "\tdey") &&
1573 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
1574 LineMatch (L2 [4], "\tldy\t#$") &&
1575 LineFullMatch (L2 [5], "\tjsr\tldauidx")) {
1577 /* Found - replace it */
1578 L2 [4]->Line [3] = 'x'; /* Change to ldx */
1579 if (LineFullMatch (L, "\tldy\t#$01")) {
1580 /* Word at offset zero */
1582 L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp");
1584 ReplaceLine (L2 [5], "\tjsr\tldauiysp");
1587 /* Delete the remaining lines */
1588 FreeLines (L2 [0], L2 [3]);
1600 if (LineFullMatch (L, "\tlda\t(sp),y") &&
1601 GetNextCodeLines (L, L2, 1) &&
1602 LineFullMatch (L2 [0], "\tjsr\tpusha")) {
1604 /* Found, replace it */
1605 L = ReplaceLine (L, "\tjsr\tpushaysp");
1609 /* All other patterns start with this one: */
1610 if (!LineFullMatch (L, "\tldx\t#$00")) {
1620 * and replace it by:
1625 if (GetNextCodeLines (L, L2, 1) &&
1626 LineFullMatch (L2 [0], "\tjsr\tpushax")) {
1628 /* Replace the subroutine call */
1629 L = ReplaceLine (L, "\tjsr\tpusha0");
1631 /* Remove the unnecessary line */
1641 * and replace it by:
1647 else if (GetNextCodeLines (L, L2, 2) &&
1648 LineMatch (L2 [0], "\tlda\t") &&
1649 LineFullMatch (L2 [1], "\tjsr\tpushax")) {
1651 /* Be sure, X is not used in the load */
1652 if (NoXAddrMode (L2 [0])) {
1654 /* Replace the subroutine call */
1655 L2 [1] = ReplaceLine (L2 [1], "\tjsr\tpusha0");
1657 /* Remove the unnecessary load */
1660 /* L must be valid */
1672 * and replace it by:
1677 else if (GetNextCodeLines (L, L2, 2) &&
1678 LineMatch (L2 [0], "\tlda\t") &&
1679 LineMatch (L2 [1], "\tcmp\t#$")) {
1681 /* Be sure, X is not used in the load */
1682 if (NoXAddrMode (L2 [0])) {
1684 /* Remove the unnecessary load */
1687 /* L must be valid */
1698 * and replace it by:
1703 else if (GetNextCodeLines (L, L2, 2) &&
1704 LineMatch (L2 [0], "\tlda\t") &&
1705 LineFullMatch (L2 [1], "\tjsr\tbnega")) {
1707 /* Be sure, X is not used in the load */
1708 if (NoXAddrMode (L2 [0])) {
1710 /* Remove the unnecessary load */
1713 /* L must be valid */
1719 /* Go to the next line */
1720 L = NextCodeLine (L);
1726 static void OptRegLoads (void)
1727 /* Remove unnecessary loads of registers */
1733 /* Repeat this until there is nothing more to delete */
1741 /* Search for a load of X and check if the value is used later */
1742 if (LineMatch (L, "\tldx\t") &&
1744 !IsCondJump (NextInstruction (L))) {
1746 /* Remember to delete this line */
1750 /* Search for a load of A and check if the value is used later */
1751 else if (LineMatch (L, "\tlda\t") &&
1753 !IsCondJump (NextInstruction (L))) {
1755 /* Remember to delete this line */
1759 /* Search for a load of Y and check if the value is used later */
1760 else if (LineMatch (L, "\tldy\t") &&
1762 !IsCondJump (NextInstruction (L))) {
1764 /* Remember to delete this line */
1768 /* Go to the next line, delete the current if requested */
1770 L = NextCodeLine (L);
1776 } while (Deletions > 0);
1781 static int OptPtrOps1 (Line** Start)
1782 /* Optimize several pointer and array constructs - subfunction 1 */
1787 unsigned LinesToRemove;
1792 /* Use a local variable for the working line */
1795 /* Search for (23B/XXT)
1807 * and replace it by something like (24B/26T)
1821 if (!LineMatch (L, "\tlda\t") ||
1822 !GetNextCodeLines (L, L2, 4) ||
1823 !IsLoadAX (L, L2 [0]) ||
1824 !LineFullMatch (L2[1], "\tsta\tregsave") ||
1825 !LineFullMatch (L2[2], "\tstx\tregsave+1")) {
1832 if (LineMatch (L2[3], "\tjsr\tincax")) {
1833 /* Get next code lines */
1834 if (GetNextCodeLines (L2[3], &L2[4], 4) == 0) {
1835 /* Cannot get lines */
1838 Inc = GetHexNum (L2[3]->Line+10);
1842 /* Get next code lines */
1843 if (GetNextCodeLines (L2[3], &L2[4], 7) == 0) {
1844 /* Cannot get lines */
1847 if (LineFullMatch (L2[3], "\tclc") &&
1848 LineMatch (L2[4], "\tadc\t#$") &&
1849 LineFullMatch (L2[5], "\tbcc\t*+3") &&
1850 LineFullMatch (L2[6], "\tinx")) {
1851 /* Inlined increment */
1852 Inc = GetHexNum (L2[4]->Line+7);
1861 /* Check for the remainder */
1862 if (!LineMatch (L3[0], "\tsta\t") ||
1863 strcmp (L3[0]->Line+5, L->Line+5) != 0 ||
1864 !LineMatch (L3[1], "\tstx\t") ||
1865 strcmp (L3[1]->Line+5, L2[0]->Line+5) != 0 ||
1866 !LineFullMatch (L3[2], "\tlda\tregsave") ||
1867 !LineFullMatch (L3[3], "\tldx\tregsave+1")) {
1873 /* Check if AX is actually used following the code above. If not,
1874 * we don't need to load A/X from regsave. Since X will never by
1875 * used without A, check just for A.
1878 if (!RegAUsed (L3[3])) {
1879 /* We don't need to load regsave */
1883 /* Special code for register variables */
1885 if (LineMatch (L, "\tlda\tregbank+") &&
1886 GetNextCodeLines (L3[3], &L3[4], 1) &&
1889 /* Remember the offset into the register bank */
1891 strcpy (Reg, L->Line+5);
1893 /* Check for several special sequences */
1894 if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
1895 /* Load char indirect */
1896 L = ReplaceLine (L, "\tldx\t#$00");
1897 L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
1898 L = NewLineAfter (L, "\tinc\t%s", Reg);
1899 L = NewLineAfter (L, "\tbne\t*+4");
1900 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1903 } else if (LineFullMatch (L3[4], "\tsta\tptr1") &&
1904 GetNextCodeLines (L3[4], &L3[5], 3) &&
1905 LineFullMatch (L3[5], "\tstx\tptr1+1") &&
1906 LineFullMatch (L3[6], "\tldx\t#$00") &&
1907 LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
1909 /* Load char indirect, inlined */
1910 L = ReplaceLine (L, "\tldx\t#$00");
1911 L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
1912 L = NewLineAfter (L, "\tinc\t%s", Reg);
1913 L = NewLineAfter (L, "\tbne\t*+4");
1914 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1918 } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
1919 if (GetNextCodeLines (L3[4], &L3[5], 2) &&
1920 LineMatch (L3[5], "\tlda\t") &&
1921 LineFullMatch (L3[6], "\tjsr\tstaspp")) {
1923 /* Store to pointer */
1924 L = ReplaceLine (L, L3[5]->Line);
1925 L = NewLineAfter (L, "\tldy\t#$00");
1926 L = NewLineAfter (L, "\tsta\t(%s),y", Reg);
1927 L = NewLineAfter (L, "\tinc\t%s", Reg);
1928 L = NewLineAfter (L, "\tbne\t*+4");
1929 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1934 } else if (GetNextCodeLines (L3[4], &L3[5], 3) &&
1935 LineMatch (L3[5], "\tldy\t#$") &&
1936 LineFullMatch (L3[6], "\tlda\t(sp),y") &&
1937 LineFullMatch (L3[7], "\tjsr\tstaspp")) {
1939 /* Beware: We have to correct the stack offset, since we will
1940 * remove the pushax instruction!
1942 Offs = GetHexNum (L3[5]->Line+7) - 2;
1944 /* Store to pointer */
1945 L = ReplaceLine (L, "\tldy\t#$%02X", Offs);
1946 L = NewLineAfter (L, "\tldx\t#$00");
1947 L = NewLineAfter (L, "\tlda\t(sp),y");
1948 L = NewLineAfter (L, "\tsta\t(%s,x)", Reg);
1949 L = NewLineAfter (L, "\tinc\t%s", Reg);
1950 L = NewLineAfter (L, "\tbne\t*+4");
1951 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1961 /* No register variable - insert the first part of the code */
1963 L = NewLineAfter (L, "\tsta\tptr1");
1965 L = NewLineAfter (L, "\tclc");
1966 L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
1967 L = NewLineAfter (L, "\tsta\t%s", L3[0]->Line+5);
1968 L = NewLineAfter (L, "\tlda\t%s", L3[1]->Line+5);
1970 L = NewLineAfter (L, "\tsta\tptr1+1");
1972 L = NewLineAfter (L, "\tadc\t#$00");
1973 L = NewLineAfter (L, "\tsta\t%s", L3[1]->Line+5);
1975 /* Check if we must really load the old value into a/x or if the
1976 * code may be replaced by something else.
1978 if (GetNextCodeLines (L3[3], &L3[4], 1)) {
1979 if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
1980 /* Load char indirect */
1981 L = NewLineAfter (L, "\tldx\t#$00");
1982 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
1985 } else if (LineFullMatch (L3[4], "\tsta\tptr1") &&
1986 GetNextCodeLines (L3[4], &L3[5], 3) &&
1987 LineFullMatch (L3[5], "\tstx\tptr1+1") &&
1988 LineFullMatch (L3[6], "\tldx\t#$00") &&
1989 LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
1991 /* Load char indirect, inlined */
1992 L = NewLineAfter (L, "\tldx\t#$00");
1993 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
1997 } else if (LineFullMatch (L3[4], "\tjsr\tldaxi")) {
1998 /* Load word indirect */
1999 L = NewLineAfter (L, "\tldy\t#$01");
2000 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2001 L = NewLineAfter (L, "\ttax");
2002 L = NewLineAfter (L, "\tdey");
2003 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2007 } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
2008 if (GetNextCodeLines (L3[4], &L3[5], 2) &&
2009 LineMatch (L3[5], "\tlda\t") &&
2010 LineFullMatch (L3[6], "\tjsr\tstaspp")) {
2012 /* Store to pointer */
2013 L = NewLineAfter (L, L3[5]->Line);
2014 L = NewLineAfter (L, "\tldy\t#$00");
2015 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2019 } else if (GetNextCodeLines (L3[4], &L3[5], 3) &&
2020 LineMatch (L3[5], "\tldy\t#$") &&
2021 LineFullMatch (L3[6], "\tlda\t(sp),y") &&
2022 LineFullMatch (L3[7], "\tjsr\tstaspp")) {
2024 /* Beware: We have to correct the stack offset, since we will
2025 * remove the pushax instruction!
2027 sprintf (L3[5]->Line+7, "%02X", GetHexNum (L3[5]->Line+7)-2);
2029 /* Store to pointer */
2030 L = NewLineAfter (L, L3[5]->Line);
2031 L = NewLineAfter (L, L3[6]->Line);
2032 L = NewLineAfter (L, "\tldy\t#$00");
2033 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2042 /* If we need to load a/x, add the code */
2044 L = NewLineAfter (L, "\ttax");
2045 L = NewLineAfter (L, "\tlda\tptr1");
2049 /* Remove the code that is no longer needed */
2050 FreeLines (L2[0], L2[LinesToRemove-1]);
2052 /* Return the new line and success */
2053 *Start = NextCodeLine (L);
2059 static int OptPtrOps2 (Line** Start)
2060 /* Optimize several pointer and array constructs - subfunction 2 */
2065 unsigned LinesToRemove;
2070 /* Use a local variable for the working line */
2073 /* Same as subfunction 1 but for local variables. */
2074 if (LineMatch (L, "\tldy\t#$") == 0) {
2078 /* Get the stack offset. The offset points to the high byte, correct that. */
2079 Offs = GetHexNum (L->Line+7) - 1;
2081 /* Check for the actual sequences */
2082 if (GetNextCodeLines (L, L2, 7) &&
2083 LineFullMatch (L2[0], "\tjsr\tldaxysp") &&
2084 LineFullMatch (L2[1], "\tsta\tregsave") &&
2085 LineFullMatch (L2[2], "\tstx\tregsave+1") &&
2086 LineMatch (L2[3], "\tjsr\tincax")) {
2088 /* Non inlined version */
2089 Inc = GetHexNum (L2[3]->Line+10);
2091 /* Check for stack offset zero */
2092 if (LineFullMatch (L2[4], "\tjsr\tstax0sp") &&
2093 LineFullMatch (L2[5], "\tlda\tregsave") &&
2094 LineFullMatch (L2[6], "\tldx\tregsave+1")) {
2098 } else if (GetNextCodeLines (L2[6], &L2[7], 1) &&
2099 LineMatch (L2[4], "\tldy\t#$") &&
2100 GetHexNum (L2[4]->Line+7) == Offs &&
2101 LineFullMatch (L2[5], "\tjsr\tstaxysp") &&
2102 LineFullMatch (L2[6], "\tlda\tregsave") &&
2103 LineFullMatch (L2[7], "\tldx\tregsave+1")) {
2112 } else if (GetNextCodeLines (L, L2, 13) &&
2113 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
2114 LineFullMatch (L2[1], "\ttax") &&
2115 LineFullMatch (L2[2], "\tdey") &&
2116 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
2117 LineFullMatch (L2[4], "\tsta\tregsave") &&
2118 LineFullMatch (L2[5], "\tstx\tregsave+1") &&
2119 LineFullMatch (L2[6], "\tclc") &&
2120 LineMatch (L2[7], "\tadc\t#$") &&
2121 LineFullMatch (L2[8], "\tbcc\t*+3") &&
2122 LineFullMatch (L2[9], "\tinx")) {
2124 /* Inlined version */
2125 Inc = GetHexNum (L2[7]->Line+7);
2127 /* Check for stack offset zero */
2128 if (LineFullMatch (L2[10], "\tjsr\tstax0sp") &&
2129 LineFullMatch (L2[11], "\tlda\tregsave") &&
2130 LineFullMatch (L2[12], "\tldx\tregsave+1")) {
2134 } else if (GetNextCodeLines (L2[12], &L2[13], 1) &&
2135 LineMatch (L2[10], "\tldy\t#$") &&
2136 GetHexNum (L2[10]->Line+7) == Offs &&
2137 LineFullMatch (L2[11], "\tjsr\tstaxysp") &&
2138 LineFullMatch (L2[12], "\tlda\tregsave") &&
2139 LineFullMatch (L2[13], "\tldx\tregsave+1")) {
2152 /* Get a pointer to the last line of the preceding sequence */
2153 L3 = &L2[LinesToRemove-1];
2155 /* Check if AX is actually used following the code above. If not,
2156 * we don't need to load A/X from regsave. Since X will never by
2157 * used without A, check just for A.
2160 if (!RegAUsed (L3[0])) {
2161 /* We don't need to load regsave */
2165 /* Replace the ldy instruction, offset must point to the low byte */
2166 sprintf (L->Line+7, "%02X", Offs);
2168 /* Insert the first part of the code */
2169 L = NewLineAfter (L, "\tlda\t(sp),y");
2171 L = NewLineAfter (L, "\tsta\tptr1");
2173 L = NewLineAfter (L, "\tclc");
2174 L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
2175 L = NewLineAfter (L, "\tsta\t(sp),y");
2176 L = NewLineAfter (L, "\tiny");
2177 L = NewLineAfter (L, "\tlda\t(sp),y");
2179 L = NewLineAfter (L, "\tsta\tptr1+1");
2181 L = NewLineAfter (L, "\tadc\t#$00");
2182 L = NewLineAfter (L, "\tsta\t(sp),y");
2184 /* Check if we must really load the old value into a/x or if the
2185 * code may be replaced by something else.
2187 if (GetNextCodeLines (L3[0], &L3[1], 1)) {
2188 if (LineFullMatch (L3[1], "\tjsr\tldaui")) {
2189 /* Load char indirect */
2190 L = NewLineAfter (L, "\tldx\t#$00");
2191 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2194 } else if (LineFullMatch (L3[1], "\tsta\tptr1") &&
2195 GetNextCodeLines (L3[1], &L3[2], 3) &&
2196 LineFullMatch (L3[2], "\tstx\tptr1+1") &&
2197 LineFullMatch (L3[3], "\tldx\t#$00") &&
2198 LineFullMatch (L3[4], "\tlda\t(ptr1,x)")) {
2200 /* Load char indirect, inlined */
2201 L = NewLineAfter (L, "\tldx\t#$00");
2202 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2206 } else if (LineFullMatch (L3[1], "\tjsr\tldaxi")) {
2207 /* Load word indirect */
2208 L = NewLineAfter (L, "\tldy\t#$01");
2209 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2210 L = NewLineAfter (L, "\ttax");
2211 L = NewLineAfter (L, "\tdey");
2212 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2216 } else if (LineFullMatch (L3[1], "\tjsr\tpushax")) {
2217 if (GetNextCodeLines (L3[1], &L3[2], 2) &&
2218 LineMatch (L3[2], "\tlda\t") &&
2219 LineFullMatch (L3[3], "\tjsr\tstaspp")) {
2221 /* Store to pointer */
2222 L = NewLineAfter (L, L3[2]->Line);
2223 L = NewLineAfter (L, "\tldy\t#$00");
2224 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2228 } else if (GetNextCodeLines (L3[1], &L3[2], 3) &&
2229 LineMatch (L3[2], "\tldy\t#$") &&
2230 LineFullMatch (L3[3], "\tlda\t(sp),y") &&
2231 LineFullMatch (L3[4], "\tjsr\tstaspp")) {
2233 /* Beware: We have to correct the stack offset, since we will
2234 * remove the pushax instruction!
2236 sprintf (L3[2]->Line+7, "%02X", GetHexNum (L3[2]->Line+7)-2);
2238 /* Store to pointer */
2239 L = NewLineAfter (L, L3[2]->Line);
2240 L = NewLineAfter (L, L3[3]->Line);
2241 L = NewLineAfter (L, "\tldy\t#$00");
2242 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2251 /* If we need to load a/x, add the code */
2253 L = NewLineAfter (L, "\ttax");
2254 L = NewLineAfter (L, "\tlda\tptr1");
2257 /* Remove the code that is no longer needed */
2258 FreeLines (L2[0], L2[LinesToRemove-1]);
2260 /* Return the new line and success */
2261 *Start = NextCodeLine (L);
2267 static void OptPtrOps (void)
2268 /* Optimize several pointer and array constructs */
2272 Line* L = FirstCode;
2275 if (OptPtrOps1 (&L)) {
2277 } else if (OptPtrOps2 (&L)) {
2281 /* Search for the following sequence:
2289 * and replace it by:
2296 else if (LineFullMatch (L, "\tlda\tregsave") && /* Match on start */
2297 GetNextCodeLines (L, L2, 4) && /* Fetch next lines */
2298 LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */
2299 LineFullMatch (L2 [1], "\tjsr\tpushax") &&
2300 LineMatch (L2 [2], "\tlda\t#$") &&
2301 LineFullMatch (L2 [3], "\tjsr\tstaspp")) {
2303 /* Found the sequence, replace it */
2304 L = ReplaceLine (L, L2 [2]->Line); /* lda #$.. */
2305 L2 [0] = ReplaceLine (L2 [0], "\tldy\t#$00");
2306 L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
2308 /* Free the remaining lines */
2309 FreeLines (L2 [2], L2 [3]);
2312 /* Search for the following sequence:
2318 * and replace it by:
2324 else if (LineFullMatch (L, "\tlda\tregsave") && /* Match on start */
2325 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
2326 LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */
2327 LineFullMatch (L2 [1], "\tjsr\tldaui")) {
2329 /* Found the sequence, replace it */
2330 L = ReplaceLine (L, "\tldx\t#$00");
2331 L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
2333 /* Free the remaining lines */
2338 * Search for the following sequence:
2347 * and replace it by:
2358 else if (LineFullMatch (L, "\tlda\tregsave") &&
2359 GetNextCodeLines (L, L2, 5) &&
2360 LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
2361 LineFullMatch (L2 [1], "\tjsr\tpushax") &&
2362 LineMatch (L2 [2], "\tldx\t#$") &&
2363 LineMatch (L2 [3], "\tlda\t#$") &&
2364 LineFullMatch (L2 [4], "\tjsr\tstaxspp")) {
2366 /* Found the sequence, replace it */
2367 L = ReplaceLine (L, "\tldy\t#$01");
2368 L2 [0] = ReplaceLine (L2 [0], L2 [2]->Line);
2369 L2 [0]->Line [3] = 'a';
2370 L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
2371 L2 [4] = ReplaceLine (L2 [4], L2 [3]->Line);
2372 L2 [2] = ReplaceLine (L2 [2], "\ttax");
2373 L2 [3] = ReplaceLine (L2 [3], "\tdey");
2374 L = NewLineAfter (L2 [4], "\tsta\t(regsave),y");
2378 * Search for the following sequence:
2387 * and replace it by:
2393 else if (LineFullMatch (L, "\tlda\tregsave") &&
2394 GetNextCodeLines (L, L2, 5) &&
2395 LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
2396 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2397 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2398 LineFullMatch (L2 [3], "\tldx\t#$00") &&
2399 LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
2401 /* Found the sequence, replace it */
2402 L = ReplaceLine (L, "\tldx\t#$00");
2403 L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
2405 /* Remove the remaining lines */
2406 FreeLines (L2 [1], L2 [4]);
2409 /* Search for the following sequence:
2415 * and replace it by:
2424 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2425 GetNextCodeLines (L, L2, 2) &&
2426 LineMatch (L2 [0], "\tlda\t") &&
2427 LineFullMatch (L2 [1], "\tjsr\tstaspp")) {
2429 /* Found the sequence, replace it */
2430 L = ReplaceLine (L, "\tsta\tptr1");
2431 L2 [1] = ReplaceLine (L2 [1], L2 [0]->Line); /* lda ... */
2432 L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
2433 L2 [2] = NewLineAfter (L2 [1], "\tldy\t#$00");
2434 L = NewLineAfter (L2 [2], "\tsta\t(ptr1),y");
2437 /* Search for the following sequence:
2444 * and replace it by:
2453 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2454 GetNextCodeLines (L, L2, 3) &&
2455 LineMatch (L2 [0], "\tlda\t") &&
2456 LineMatch (L2 [1], "\tldy\t#$") &&
2457 LineFullMatch (L2 [2], "\tjsr\tstaspidx")) {
2459 /* Found the sequence, replace it */
2460 L = ReplaceLine (L, "\tsta\tptr1");
2461 L = NewLineAfter (L, "\tstx\tptr1+1");
2462 L2 [2] = ReplaceLine (L2 [2], "\tsta\t(ptr1),y");
2465 /* Search for the following sequence:
2472 * and replace it by:
2481 * Beware: Since we remove a call to a function that changes the stack
2482 * pointer, we have to adjust the stack address for the lda.
2485 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2486 GetNextCodeLines (L, L2, 3) &&
2487 LineMatch (L2 [0], "\tldy\t#$") &&
2488 LineFullMatch (L2 [1], "\tlda\t(sp),y") &&
2489 LineFullMatch (L2 [2], "\tjsr\tstaspp")) {
2491 /* Found the sequence, replace it. First create a new load
2492 * instruction for the changed stack offset.
2495 sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
2496 L = ReplaceLine (L, "\tsta\tptr1");
2497 L2 [1] = ReplaceLine (L2 [1], Buf); /* ldy ... */
2498 L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
2499 L2 [2] = ReplaceLine (L2 [2], "\tlda\t(sp),y");
2500 L2 [3] = NewLineAfter (L2 [2], "\tldy\t#$00");
2501 L = NewLineAfter (L2 [3], "\tsta\t(ptr1),y");
2504 /* Search for the following sequence:
2512 * and replace it by:
2521 * Beware: Since we remove a call to a function that changes the stack
2522 * pointer, we have to adjust the stack address for the lda.
2525 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2526 GetNextCodeLines (L, L2, 4) &&
2527 LineMatch (L2 [0], "\tldy\t#$") &&
2528 LineFullMatch (L2 [1], "\tlda\t(sp),y") &&
2529 LineMatch (L2 [2], "\tldy\t#$") &&
2530 LineFullMatch (L2 [3], "\tjsr\tstaspidx")) {
2532 /* Found the sequence, replace it. First create a new load
2533 * instruction for the changed stack offset.
2536 sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
2537 L = ReplaceLine (L, "\tsta\tptr1");
2538 L = NewLineAfter (L, "\tstx\tptr1+1");
2539 L2 [0] = ReplaceLine (L2 [0], Buf); /* ldy ... */
2540 L2 [3] = ReplaceLine (L2 [3], "\tsta\t(ptr1),y");
2543 /* Search for the following sequence:
2556 * and replace it by:
2564 * The load of X may be omitted if X is not used below.
2566 else if (LineMatch (L, "\tldax\t_") &&
2567 GetNextCodeLines (L, L2, 9) &&
2568 LineMatch (L2 [0], "\tldy\t#$") &&
2569 LineFullMatch (L2 [1], "\tclc") &&
2570 LineFullMatch (L2 [2], "\tadc\t(sp),y") &&
2571 LineFullMatch (L2 [3], "\tbcc\t*+3") &&
2572 LineFullMatch (L2 [4], "\tinx") &&
2573 LineFullMatch (L2 [5], "\tsta\tptr1") &&
2574 LineFullMatch (L2 [6], "\tstx\tptr1+1") &&
2575 LineFullMatch (L2 [7], "\tldx\t#$00") &&
2576 LineFullMatch (L2 [8], "\tlda\t(ptr1,x)")) {
2578 /* Found the sequence, replace it */
2580 strcpy (Label, L->Line + 6); /* Remember the label */
2581 L = ReplaceLine (L, L2 [0]->Line); /* ldy .. */
2582 L = NewLineAfter (L, "\tlda\t(sp),y");
2583 L = NewLineAfter (L, "\ttay");
2584 if (RegXUsed (L2[8])) {
2585 L = NewLineAfter (L, "\tldx\t#$00");
2587 L = NewLineAfter (L, "\tlda\t%s,y", Label);
2589 /* Remove the remaining stuff. There may be hints between the
2590 * instructions, remove them too
2592 FreeLines (L2[0], L2 [8]);
2616 * This change will cost 2 cycles, but it saves a lot of code (6 bytes
2617 * per occurrence), so we will accept the overhead. It may even be
2618 * possible to rewrite the library routine to get rid of the additional
2621 else if (LineMatch (L, "\tldy\t#$") &&
2622 GetNextCodeLines (L, L2, 6) &&
2623 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
2624 LineFullMatch (L2 [1], "\ttax") &&
2625 LineFullMatch (L2 [2], "\tdey") &&
2626 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
2627 LineMatch (L2 [4], "\tldy\t#$") &&
2628 LineFullMatch (L2 [5], "\tjsr\tldauidx")) {
2630 /* Found - replace it */
2631 L2 [4]->Line [3] = 'x'; /* Change to ldx */
2632 if (LineFullMatch (L, "\tldy\t#$01")) {
2633 /* Word at offset zero */
2635 L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp");
2637 ReplaceLine (L2 [5], "\tjsr\tldauiysp");
2640 /* Delete the remaining lines */
2641 FreeLines (L2 [0], L2 [3]);
2665 * This change will has an overhead of 10 cycles, but it saves 11(!)
2666 * bytes per invocation. Maybe we should apply only if FavourSize is
2669 else if (LineMatch (L, "\tldy\t#$") &&
2670 GetNextCodeLines (L, L2, 8) &&
2671 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
2672 LineFullMatch (L2 [1], "\ttax") &&
2673 LineFullMatch (L2 [2], "\tdey") &&
2674 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
2675 LineFullMatch (L2 [4], "\tsta\tptr1") &&
2676 LineFullMatch (L2 [5], "\tstx\tptr1+1") &&
2677 LineFullMatch (L2 [6], "\tldx\t#$00") &&
2678 LineFullMatch (L2 [7], "\tlda\t(ptr1,x)")) {
2680 /* Found - replace it */
2681 if (LineFullMatch (L, "\tldy\t#$01")) {
2682 /* Word at offset zero */
2684 L = ReplaceLine (L2 [0], "\tjsr\tldau00sp");
2686 ReplaceLine (L2 [0], "\tjsr\tldau0ysp");
2689 /* Delete the remaining lines */
2690 FreeLines (L2 [1], L2 [7]);
2694 L = NextCodeLine (L);
2700 static void OptRegVars (void)
2701 /* Optimize register variable uses */
2705 Line* L = FirstCode;
2708 /* Search for the following sequence:
2714 * and replace it by:
2720 if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2721 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
2722 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2723 LineFullMatch (L2 [1], "\tjsr\tldaui") &&
2724 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2725 strcmp (L2 [0]->Line + 14, "+1") == 0) {
2728 sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
2730 /* Found the sequence, replace it */
2731 L = ReplaceLine (L, "\tldx\t#$00");
2732 L2 [0] = ReplaceLine (L2 [0], Buf);
2734 /* Free the remaining lines */
2738 /* Search for the following sequence:
2747 * and replace it by:
2753 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2754 GetNextCodeLines (L, L2, 5) && /* Fetch next lines */
2755 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2756 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2757 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2758 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2759 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2760 LineFullMatch (L2 [3], "\tldx\t#$00") &&
2761 LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
2764 sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
2766 /* Found the sequence, replace it */
2767 L = ReplaceLine (L, "\tldx\t#$00");
2768 L2 [0] = ReplaceLine (L2 [0], Buf);
2770 /* Free the remaining lines */
2771 FreeLines (L2 [1], L2 [4]);
2774 /* Search for the following sequence:
2781 * and replace it by:
2788 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2789 GetNextCodeLines (L, L2, 3) && /* Fetch next lines */
2790 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2791 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2792 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2793 LineMatch (L2 [1], "\tldy\t#$") &&
2794 LineFullMatch (L2 [2], "\tjsr\tldauidx")) {
2797 sprintf (Buf, "\tlda\t(%s),y", L->Line + 5);
2799 /* Found the sequence, replace it */
2800 L = ReplaceLine (L, L2 [1]->Line);
2801 L2 [0] = ReplaceLine (L2 [0], "\tldx\t#$00");
2802 L2 [1] = ReplaceLine (L2 [1], Buf);
2804 /* Free the remaining lines */
2808 /* Search for the following sequence:
2818 * and replace it by:
2824 * The source form is not generated by the parser but by the optimizer.
2826 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2827 GetNextCodeLines (L, L2, 6) && /* Fetch next lines */
2828 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2829 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2830 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2831 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2832 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2833 LineMatch (L2 [3], "\tlda\t") &&
2834 LineMatch (L2 [4], "\tldy\t#$") &&
2835 LineMatch (L2 [5], "\tsta\t(ptr1),y")) {
2838 sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
2840 /* Found the sequence, replace it */
2841 L2 [5] = ReplaceLine (L2 [5], Buf);
2843 /* Free the remaining lines */
2844 FreeLines (L, L2 [2]);
2846 /* Make the line pointer valid again */
2850 /* Search for the following sequence:
2861 * and replace it by:
2868 * The source form is not generated by the parser but by the optimizer.
2870 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2871 GetNextCodeLines (L, L2, 7) && /* Fetch next lines */
2872 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2873 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2874 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2875 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2876 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2877 LineMatch (L2 [3], "\tldy\t#$") &&
2878 LineFullMatch (L2 [4], "\tlda\t(sp),y") &&
2879 LineMatch (L2 [5], "\tldy\t#$") &&
2880 LineMatch (L2 [6], "\tsta\t(ptr1),y")) {
2883 sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
2885 /* Found the sequence, replace it */
2886 L2 [6] = ReplaceLine (L2 [6], Buf);
2888 /* Free the remaining lines */
2889 FreeLines (L, L2 [2]);
2891 /* Make the line pointer valid again */
2896 L = NextCodeLine (L);
2902 static void OptDoubleJumps (void)
2903 /* Remove/rearrange jumps that jump to other jumps */
2905 static const char* Jumps [] = {
2916 Line* L = FirstCode;
2921 /* Is this a jump? */
2922 while ((I = LineMatchX (L, Jumps)) >= 0) {
2924 /* Yes. Get the target label */
2925 Line* Target = GetTargetLine (L->Line + 5);
2927 /* Target points to the label itself. Skip lines until we reach
2928 * one that is not a label.
2930 Target = NextInstruction (Target);
2932 /* Be sure, this line is not the same as the one the jump is
2933 * in (this happens if there is an empty loop).
2939 if (LineMatch (Target, "\tjmp\t")) {
2941 /* The target is itself a jump. If this is a short branch, get
2942 * the final target and check if it is in reach. Bail out if
2945 if (L->Line[1] == 'b') {
2946 Line* FinalTarget = GetTargetLine (Target->Line+5);
2947 FinalTarget = NextInstruction (FinalTarget);
2948 if ((D = GetJumpDistance (L, FinalTarget)) >= 123) {
2953 /* Make sure the jump does indeed point to another label.
2954 * It may happen that this is not the case for some endless
2955 * loop (while(1) and similar).
2957 if (strcmp (L->Line+5, Target->Line+5) == 0) {
2958 /* Same label, bail out */
2962 /* Use the label in the original jump instead */
2963 L = ReplaceLine (L, "%.5s%s", L->Line, Target->Line+5);
2965 } else if (I < 2 && LineMatch (Target, Jumps [I])) {
2967 /* Conditional jump. Use final label */
2968 strcpy (L->Line+5, Target->Line+5);
2976 L = NextCodeLine (L);
2982 static void OptJumpRTS (void)
2983 /* Replace jumps to an RTS by an RTS */
2985 Line* L = FirstCode;
2987 /* Is this a jump to a numbered label? */
2988 if (LineMatch (L, "\tjmp\t") && L->Line [5] == 'L' && isdigit (L->Line [6])) {
2990 /* Yes. Get the target label */
2991 Line* Target = GetTargetLine (L->Line+5);
2993 /* Target points to the label itself. Get the next line */
2994 Target = NextCodeLine (Target);
2995 if (LineFullMatch (Target, "\trts")) {
2996 /* Replace the jump by an RTS */
2997 L = ReplaceLine (L, "\trts");
3000 L = NextCodeLine (L);
3006 static void OptBoolTransforms (void)
3007 /* Try to remove the boolean transformation subroutines where they aren't
3013 const char* BranchTarget;
3015 Line* L = FirstCode;
3018 /* Search for a boolean transformer followed by a conditional jump. */
3019 if (LineMatch (L, "\tjsr\tbool") &&
3020 GetNextCodeLines (L, L2, 1) &&
3021 IsCondJump (L2 [0])) {
3023 /* Make the boolean transformer unnecessary by changing the
3024 * the conditional jump to evaluate the condition flags that
3025 * are set after the compare directly. Note: jeq jumps if
3026 * the condition is not met, jne jumps if the condition is met.
3029 /* Get the condition code */
3030 int Cond = FindCond (L->Line + 9);
3036 /* Invert the code if we jump on condition not met. */
3037 if (L2[0]->Line [2] == 'e' && L2[0]->Line [3] == 'q') {
3038 /* Jumps if condition false, invert condition */
3039 Cond = CmpInvertTab [Cond];
3042 /* For easier reading, get a pointer to the jump target */
3043 BranchTarget = L2[0]->Line+5;
3045 /* Check if we can replace the jump (sometimes we would need two
3046 * conditional jumps, we will not handle that for now since it
3047 * has some complications - both jumps may be far jumps for
3048 * example making the jumps more costly than the bool transformer
3049 * subroutine). If we cannot replace the jump, bail out.
3054 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3058 L = ReplaceLine (L, "\tjne\t%s", BranchTarget);
3062 Label = AllocLabel ();
3063 L = ReplaceLine (L, "\tbeq\tL%04X", Label);
3064 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3065 L = NewLabelAfter(L, Label);
3069 L = ReplaceLine (L, "\tjpl\t%s", BranchTarget);
3073 L = ReplaceLine (L, "\tjmi\t%s", BranchTarget);
3077 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3078 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3082 Label = AllocLabel ();
3083 L = ReplaceLine (L, "\tbeq\tL%04X", Label);
3084 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3085 L = NewLabelAfter(L, Label);
3089 L = ReplaceLine (L, "\tjcs\t%s", BranchTarget);
3093 L = ReplaceLine (L, "\tjcc\t%s", BranchTarget);
3097 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3098 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3102 Internal ("Unknown jump condition: %u", Cond);
3106 /* Remove the old stuff */
3112 L = NextCodeLine (L);
3118 static void OptCompares2 (void)
3119 /* Try to optimize the integer compare subroutines. */
3123 const char* BranchTarget;
3126 Line* L = FirstCode;
3144 if (LineMatch (L, "\tlda\t") &&
3145 GetNextCodeLines (L, L2, 5) &&
3146 IsLoadAX (L, L2[0]) &&
3147 LineFullMatch (L2[1], "\tcpx\t#$00") &&
3148 LineFullMatch (L2[2], "\tbne\t*+4") &&
3149 LineFullMatch (L2[3], "\tcmp\t#$00") &&
3150 IsCondJump (L2[4])) {
3152 /* Replace the load of X by an ora */
3153 L2[0]->Line[1] = 'o';
3154 L2[0]->Line[2] = 'r';
3155 L2[0]->Line[3] = 'a';
3157 /* Remove unneeded stuff */
3158 FreeLines (L2[1], L2[3]);
3162 /* Same for local variables: Replace
3182 else if (LineMatch (L, "\tldy\t#$") &&
3183 GetNextCodeLines (L, L2, 8) &&
3184 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
3185 LineFullMatch (L2[1], "\ttax") &&
3186 LineFullMatch (L2[2], "\tdey") &&
3187 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
3188 LineFullMatch (L2[4], "\tcpx\t#$00") &&
3189 LineFullMatch (L2[5], "\tbne\t*+4") &&
3190 LineFullMatch (L2[6], "\tcmp\t#$00") &&
3191 IsCondJump (L2[7])) {
3193 /* Replace the second load by an ora */
3194 L2[3]->Line[1] = 'o';
3195 L2[3]->Line[2] = 'r';
3196 L2[3]->Line[3] = 'a';
3198 /* Remove unneeded stuff */
3200 FreeLines (L2[4], L2[6]);
3204 /* Search for the call to a compare subroutine followed by a
3207 else if (LineMatch (L, "\tjsr\ttos") &&
3208 (L2[0] = NextCodeLine (L)) != 0 &&
3209 IsCondJump (L2[0])) {
3211 /* Extract the condition from the function name and branch */
3212 C = CheckAndGetIntCmp (L, L2[0]);
3214 /* Something is wrong */
3218 /* Replace the subroutine call by a cheaper one */
3219 L = ReplaceLine (L, "\tjsr\ttosicmp");
3221 /* For easier reading, get a pointer to the jump target */
3222 BranchTarget = L2[0]->Line+5;
3224 /* Check if we can replace the jump (sometimes we would need two
3225 * conditional jumps, we will not handle that for now since it
3226 * has some complications - both jumps may be far jumps for
3227 * example making the jumps more costly than the bool transformer
3228 * subroutine). If we cannot replace the jump, bail out.
3233 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3237 L = NewLineAfter (L, "\tjne\t%s", BranchTarget);
3241 Label = AllocLabel ();
3242 L = NewLineAfter (L, "\tbeq\tL%04X", Label);
3243 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3244 L = NewLabelAfter(L, Label);
3248 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3252 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3256 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3257 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3261 Label = AllocLabel ();
3262 L = NewLineAfter (L, "\tbeq\tL%04X", Label);
3263 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3264 L = NewLabelAfter(L, Label);
3268 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3272 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3276 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3277 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3281 Internal ("Unknown jump condition: %u", C);
3285 /* Remove the old stuff */
3290 L = NextCodeLine (L);
3296 static void OptTests (void)
3297 /* Remove unnecessary tests */
3301 const char* BitOps [] = {
3308 /* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
3315 Line* L = FirstCode;
3318 /* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
3325 if ((LineMatch (L, "\tlda\t") ||
3326 LineMatch (L, "\tand\t") ||
3327 LineMatch (L, "\tora\t") ||
3328 LineMatch (L, "\teor\t")) &&
3329 GetNextCodeLines (L, L2, 2) &&
3330 (LineFullMatch (L2 [0], "\ttay") ||
3331 LineFullMatch (L2 [0], "\tcmp\t#$00")) &&
3332 IsCondJump (L2 [1])) {
3334 /* We can remove the tay */
3345 * and remove the tax.
3347 else if (LineMatchX (L, BitOps) >= 0 &&
3348 GetNextCodeLines (L, L2, 2) &&
3349 LineFullMatch (L2[0], "\ttax") &&
3350 IsCondJump (L2[1])) {
3352 /* Remove the tax including a hint line of there is one */
3353 if (LineFullMatch (L2[0]->Prev, "+forcetest")) {
3354 FreeLine (L2[0]->Prev);
3358 /* If the line before L loads X, this is useless and may be removed */
3359 L2[0] = PrevCodeLine (L);
3360 if (LineFullMatch (L2[0], "\tldx\t#$00")) {
3366 /* Search for the sequence
3377 else if (LineMatch (L, "\tstx\t") &&
3378 GetNextCodeLines (L, L2, 2) &&
3379 LineFullMatch (L2[0], "\tstx\ttmp1") &&
3380 LineFullMatch (L2[1], "\tora\ttmp1")) {
3382 /* Found, replace it */
3383 L = NewLineAfter (L, "\tora\t%s", L->Line+5);
3385 /* Remove remaining stuff */
3386 FreeLines (L2[0], L2[1]);
3392 L = NextCodeLine (L);
3398 static void OptNeg (void)
3399 /* Optimize the "bnegax/jeq" and "bnegax/jne" sequences */
3403 Line* L = FirstCode;
3406 /* Search for the sequence:
3412 * and replace it by:
3417 if (LineMatch (L, "\tlda\t") && /* Match on start */
3418 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
3419 LineFullMatch (L2 [0], "\tjsr\tbnega") &&
3420 IsCondJump (L2 [1])) {
3422 /* Found the sequence, replace it */
3424 InvertZJump (L2 [1]);
3428 /* Search for the sequence:
3446 else if (LineMatch (L, "\tldy\t#$") &&
3447 GetNextCodeLines (L, L2, 6) &&
3448 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
3449 LineFullMatch (L2[1], "\ttax") &&
3450 LineFullMatch (L2[2], "\tdey") &&
3451 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
3452 LineFullMatch (L2[4], "\tjsr\tbnegax") &&
3453 IsCondJump (L2[5])) {
3455 L2[1] = ReplaceLine (L2[1], "\tdey");
3456 L2[2] = ReplaceLine (L2[2], "\tora\t(sp),y");
3457 FreeLines (L2[3], L2[4]);
3458 InvertZJump (L2[5]);
3462 /* Search for the sequence:
3475 else if (LineMatch (L, "\tlda\t") &&
3476 GetNextCodeLines (L, L2, 3) &&
3477 IsLoadAX (L, L2[0]) &&
3478 LineFullMatch (L2[1], "\tjsr\tbnegax") &&
3479 IsCondJump (L2[2])) {
3481 /* Replace the load of X by ora */
3482 L2[0]->Line[1] = 'o';
3483 L2[0]->Line[2] = 'r';
3484 L2[0]->Line[3] = 'a';
3486 InvertZJump (L2[2]);
3490 /* Search for the sequence:
3496 * and replace it by:
3502 else if (LineMatch (L, "\tjsr\t_") && /* Match on start */
3503 GetNextCodeLines (L, L2, 2) &&
3504 LineMatch (L2 [0], "\tjsr\tbnega") &&
3505 IsCondJump (L2 [1])) {
3507 if (LineFullMatch (L2 [0], "\tjsr\tbnega")) {
3509 L2 [0] = ReplaceLine (L2 [0], "\ttax"); /* Test a */
3512 L2 [0] = ReplaceLine (L2 [0], "\tstx\ttmp1");
3513 NewLineAfter (L2 [0], "\tora\ttmp1");
3516 /* Invert the jump */
3517 InvertZJump (L2 [1]);
3522 L = NextCodeLine (L);
3528 static void OptTriples (void)
3529 /* Replace code triples */
3531 static const char* Pat1 [] = {
3539 static const char* Pat2 [] = {
3547 static const char* Replace [] = {
3555 Line* L = FirstCode;
3557 int I = LineFullMatchX (L, Pat1);
3559 /* We found the first match, get the next line */
3560 Line* L2 = NextCodeLine (L);
3561 if (L2 && LineFullMatch (L2, Pat2 [I])) {
3562 /* Found. Replace by the short call */
3564 L = ReplaceLine (L, Replace [I]);
3568 L = NextCodeLine (L);
3574 static Line* OptOneBlock (Line* L)
3575 /* Optimize the register contents inside one basic block */
3577 static const char* Compares [] = {
3578 "\tjsr\ttoseq00", "\tjsr\ttoseqa0", "\tjsr\ttoseqax",
3579 "\tjsr\ttoseqeax", "\tjsr\ttosne00", "\tjsr\ttosnea0",
3580 "\tjsr\ttosneax", "\tjsr\ttosneeax", "\tjsr\ttoslt00",
3581 "\tjsr\ttoslta0", "\tjsr\ttosltax", "\tjsr\ttosult00",
3582 "\tjsr\ttosulta0", "\tjsr\ttosultax", "\tjsr\ttoslteax",
3583 "\tjsr\ttosulteax", "\tjsr\ttosle00", "\tjsr\ttoslea0",
3584 "\tjsr\ttosleax", "\tjsr\ttosule00", "\tjsr\ttosulea0",
3585 "\tjsr\ttosuleax", "\tjsr\ttosleeax", "\tjsr\ttosuleeax",
3586 "\tjsr\ttosgt00", "\tjsr\ttosgta0", "\tjsr\ttosgtax",
3587 "\tjsr\ttosugt00", "\tjsr\ttosugta0", "\tjsr\ttosugtax",
3588 "\tjsr\ttosgteax", "\tjsr\ttosugteax", "\tjsr\ttosge00",
3589 "\tjsr\ttosgea0", "\tjsr\ttosgeax", "\tjsr\ttosuge00",
3590 "\tjsr\ttosugea0", "\tjsr\ttosugeax", "\tjsr\ttosgeeax",
3595 static const char* MakeBool [] = {
3596 "\tjsr\tbooleq", "\tjsr\tboolne", "\tjsr\tboollt",
3597 "\tjsr\tboolle", "\tjsr\tboolgt", "\tjsr\tboolge",
3598 "\tjsr\tboolult", "\tjsr\tboolule", "\tjsr\tboolugt",
3603 int A = -1; /* Contents of A register */
3604 int X = -1; /* Contents of X register */
3605 int Y = -1; /* Contents of Y register */
3610 while (L && !IsLabel (L)) {
3612 /* Handle all instructions. All instructions not tested here have
3613 * no effects on the register contents.
3616 if (L->Line [0] == '+') {
3617 /* This is a hint */
3618 if (LineMatch (L, "+a:")) {
3619 /* Information about a */
3620 switch (L->Line [3]) {
3621 case '!': A = -1; break;
3622 case '=': A = GetHexNum (L->Line + 4); break;
3624 } else if (LineMatch (L, "+x:")) {
3625 /* The code generator tells something about the x register */
3626 switch (L->Line [3]) {
3627 case '!': X = -1; break;
3628 case '=': X = GetHexNum (L->Line + 4); break;
3630 } else if (LineMatch (L, "+y:")) {
3631 /* Information about the y register */
3632 switch (L->Line [3]) {
3633 case '!': Y = -1; break;
3634 case '=': Y = GetHexNum (L->Line + 4); break;
3637 } else if (LineMatch (L, "\tadc\t")) {
3639 } else if (LineMatch (L, "\tand\t")) {
3641 } else if (LineFullMatch (L, "\tasl\ta")) {
3643 A = (A << 1) & 0xFF;
3645 } else if (CPU == CPU_65C02 && LineFullMatch (L, "\tdea")) {
3647 } else if (LineFullMatch (L, "\tdex")) {
3649 } else if (LineFullMatch (L, "\tdey")) {
3651 } else if (LineMatch (L, "\teor")) {
3653 } else if (CPU == CPU_65C02 && LineFullMatch (L, "\tina")) {
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\tpushc0")) {
3871 /* We know about this function */
3874 } else if (LineFullMatch (L, "\tjsr\tpushc1")) {
3875 /* We know about this function */
3878 } else if (LineFullMatch (L, "\tjsr\tpushc2")) {
3879 /* We know about this function */
3882 } else if (LineFullMatch (L, "\tjsr\tpushw")) {
3883 /* We know about this function (calls pushax) */
3886 } else if (LineFullMatch (L, "\tjsr\tpushw0sp")) {
3887 /* We know about this function(calls pushax) */
3890 } else if (LineFullMatch (L, "\tjsr\tpushwidx")) {
3891 /* We know about this function (calls pushax) */
3894 } else if (LineFullMatch (L, "\tjsr\tpushwysp")) {
3895 /* We know about this function (calls pushax) */
3898 } else if (LineFullMatch (L, "\tjsr\tresteax")) {
3899 /* We know about this function */
3901 } else if (LineFullMatch (L, "\tjsr\tsaveeax")) {
3902 /* We know about this function */
3903 /* Changes nothing */
3904 } else if (LineFullMatch (L, "\tjsr\tshrax1")) {
3905 /* We know about this function */
3907 } else if (LineFullMatch (L, "\tjsr\tshrax2")) {
3908 /* We know about this function */
3910 } else if (LineFullMatch (L, "\tjsr\tshrax3")) {
3911 /* We know about this function */
3913 } else if (LineFullMatch (L, "\tjsr\tshreax1")) {
3914 /* We know about this function */
3916 } else if (LineFullMatch (L, "\tjsr\tshreax2")) {
3917 /* We know about this function */
3919 } else if (LineFullMatch (L, "\tjsr\tshreax3")) {
3920 /* We know about this function */
3922 } else if (LineFullMatch (L, "\tjsr\tstaspp")) {
3923 /* We know about this function */
3925 } else if (LineFullMatch (L, "\tjsr\tstaxspp")) {
3926 /* We know about this function */
3928 } else if (LineFullMatch (L, "\tjsr\tstax0sp")) {
3929 /* We know about this function */
3931 } else if (LineFullMatch (L, "\tjsr\tstaxysp")) {
3932 /* We know about this function */
3934 } else if (LineFullMatch (L, "\tjsr\tsubeq0sp")) {
3935 /* We know about this function */
3938 } else if (LineFullMatch (L, "\tjsr\tsubeqysp")) {
3939 /* We know about this function */
3942 } else if (LineFullMatch (L, "\tjsr\ttosadda0")) {
3943 /* We know about this function */
3946 } else if (LineFullMatch (L, "\tjsr\ttosaddax")) {
3947 /* We know about this function */
3950 } else if (LineFullMatch (L, "\tjsr\ttosicmp")) {
3951 /* We know about this function */
3954 } else if (LineFullMatchX (L, Compares) >= 0) {
3957 } else if (LineFullMatchX (L, MakeBool) >= 0) {
3960 } else if (LineMatch (L, "\tjsr\t")) {
3961 /* Subroutine call, forget all register information */
3963 } else if (LineMatch (L, "\tlda\t")) {
3964 if (!RegAUsed (L) && !IsCondJump (NextInstruction (L))) {
3965 /* The value loaded is not used later, remove it */
3967 } else if (LineMatch (L, "\tlda\t(")) {
3968 if (IsXAddrMode (L)) {
3969 /* lda (zp,x) - if Y and X are both zero, replace by
3970 * load indirect y and save one cycle in some cases.
3972 if (X == 0 && Y == 0) {
3974 const char* S = L->Line + 6;
3976 strcpy (Buf, "\tlda\t(");
3984 L = ReplaceLine (L, Buf);
3987 /* In any case invalidate A */
3989 } else if (LineMatch (L, "\tlda\t#$")) {
3990 /* Immidiate load into A */
3991 NewVal = GetHexNum (L->Line + 7);
3993 /* Load has no effect */
3995 } else if (NewVal == X) {
3996 /* Requested value is already in X */
3997 L = ReplaceLine (L, "\ttxa");
3998 } else if (NewVal == Y) {
3999 /* Requested value is already in Y */
4000 L = ReplaceLine (L, "\ttya");
4001 } else if (CPU == CPU_65C02 && A != -1) {
4002 /* Try ina/dea operators of 65C02 */
4003 if (NewVal == ((A - 1) & 0xFF)) {
4004 L = ReplaceLine (L, "\tdea");
4005 } else if (NewVal == ((A + 1) & 0xFF)) {
4006 L = ReplaceLine (L, "\tina");
4009 /* Anyway, the new value is now in A */
4012 /* Memory load into A */
4015 } else if (LineMatch (L, "\tldax\t")) {
4016 /* Memory load into A and X */
4018 } else if (LineMatch (L, "\tldx\t")) {
4019 if (!RegXUsed (L) && !IsCondJump (NextInstruction (L))) {
4020 /* The value loaded is not used later, remove it */
4022 } else if (LineMatch (L, "\tldx\t#$")) {
4023 /* Immidiate load into X */
4024 NewVal = GetHexNum (L->Line + 7);
4026 /* Load has no effect */
4028 } else if (NewVal == A) {
4029 /* Requested value is already in A */
4030 L = ReplaceLine (L, "\ttax");
4031 } else if (X != -1 && NewVal == ((X + 1) & 0xFF)) {
4032 /* Requested value is one more than current contents */
4033 L = ReplaceLine (L, "\tinx");
4034 } else if (X != -1 && NewVal == ((X - 1) & 0xFF)) {
4035 /* Requested value is one less than current contents */
4036 L = ReplaceLine (L, "\tdex");
4038 /* Anyway, the new value is now in X */
4041 /* Memory load into X */
4044 } else if (LineMatch (L, "\tldy\t")) {
4045 if (!RegYUsed (L) && !IsCondJump (NextInstruction (L))) {
4046 /* The value loaded is not used later, remove it */
4048 } else if (LineMatch (L, "\tldy\t#$")) {
4049 /* Immidiate load into Y */
4050 NewVal = GetHexNum (L->Line + 7);
4052 /* Load has no effect */
4054 } else if (NewVal == A) {
4055 /* Requested value is already in A */
4056 L = ReplaceLine (L, "\ttay");
4057 } else if (Y != -1 && NewVal == ((Y + 1) & 0xFF)) {
4058 /* Requested value is one more than current contents */
4059 L = ReplaceLine (L, "\tiny");
4060 } else if (Y != -1 && NewVal == ((Y - 1) & 0xFF)) {
4061 /* Requested value is one less than current contents */
4062 L = ReplaceLine (L, "\tdey");
4064 /* Anyway, the new value is now in Y */
4067 /* Memory load into Y */
4070 } else if (LineFullMatch (L, "\tlsr\ta")) {
4074 } else if (LineMatch (L, "\tora\t#$")) {
4076 A |= GetHexNum (L->Line + 7);
4078 } else if (LineMatch (L, "\tora\t")) {
4080 } else if (LineFullMatch (L, "\tpla")) {
4082 } else if (LineFullMatch (L, "\trol\ta")) {
4084 } else if (LineFullMatch (L, "\tror\ta")) {
4086 } else if (LineFullMatch (L, "\trts")) {
4088 } else if (LineFullMatch (L, "\trti")) {
4090 } else if (LineMatch (L, "\tsbc\t")) {
4092 } else if (CPU == CPU_65C02 && LineMatch (L, "\tst")) {
4093 /* Try to replace by stz if possible */
4094 if (A == 0 && LineMatch (L, "\tsta\t")) {
4095 /* Not indirect and not Y allowed */
4096 if (L->Line[5] != '(' && !IsYAddrMode (L)) {
4099 } else if (X == 0 && LineMatch (L, "\tstx\t")) {
4100 /* absolute,y not allowed */
4101 if (!IsYAddrMode (L)) {
4104 } else if (Y == 0 && LineMatch (L, "\tsty\t")) {
4105 /* sty and stz share all addressing modes */
4108 } else if (LineFullMatch (L, "\ttax")) {
4109 if (A != -1 && X == A) {
4110 /* Load has no effect */
4115 } else if (LineFullMatch (L, "\ttay")) {
4116 if (A != -1 && Y == A) {
4117 /* Load has no effect */
4122 } else if (LineFullMatch (L, "\ttsx")) {
4124 } else if (LineFullMatch (L, "\ttxa")) {
4125 if (X != -1 && A == X) {
4126 /* Load has no effect */
4131 } else if (LineFullMatch (L, "\ttya")) {
4132 if (Y != -1 && A == Y) {
4133 /* Load has no effect */
4140 /* Set to next line, handle deletions */
4141 L2 = NextCodeSegLine (L);
4149 /* Skip the label */
4150 L = NextCodeSegLine (L);
4157 static void OptBlocks (void)
4158 /* Optimize the register contents inside basic blocks */
4160 Line* L = FirstCode;
4162 L = OptOneBlock (L);
4168 static void OptJumps (void)
4169 /* Optimize jumps */
4171 static const char* Jumps [] = {
4181 Line* L = FirstCode;
4183 int I = LineMatchX (L, Jumps);
4185 Line* Target = GetTargetLine (L->Line+5);
4186 if (Target->Index > L->Index) {
4187 /* This is a forward jump. Backward jumps are handled
4188 * automagically by the assembler.
4190 unsigned Distance = GetJumpDistance (L, Target);
4191 if (Distance < 123) { /* Safety */
4192 L->Line [1] = 'b'; /* Make a short branch */
4193 L->Size = 2; /* Set new size */
4197 L = NextCodeLine (L);
4203 static void OptRTS (void)
4204 /* Change sequences of jsr XXX/rts to jmp XXX */
4206 Line* L = FirstCode;
4208 if (LineMatch (L, "\tjsr\t")) {
4209 /* This is a jsr, get the next instruction */
4210 Line* L2 = NextCodeLine (L);
4211 if (L2 && LineFullMatch (L2, "\trts")) {
4212 /* We found a sequence */
4218 /* Try the next line */
4219 L = NextCodeLine (L);
4225 /*****************************************************************************/
4227 /*****************************************************************************/
4231 static void OptOnePass (unsigned long Flag, void (*F) (void))
4232 /* Call one optimizer pass if enabled */
4234 if ((OptDisable & Flag) == 0) {
4236 } else if (Verbose || Debug) {
4237 printf ("Optimizer pass %04lX skipped\n", Flag);
4243 void OptDoOpt (void)
4244 /* Run the optimizer over the collected stuff */
4246 /* Find and remember the first line of code */
4249 /* Mark all lines inside the code segment */
4252 /* Create a list of all local labels for fast access */
4255 /* Ok, now start the real optimizations */
4257 /* Optimize compares - first step */
4258 OptOnePass (0x0001, OptCompares1);
4260 /* Remove dead jumps */
4261 OptOnePass (0x0002, OptDeadJumps);
4263 /* Remove unnecessary loads */
4264 OptOnePass (0x0004, OptLoads);
4266 /* Remove unnecessary register loads */
4267 OptOnePass (0x0008, OptRegLoads);
4269 /* Optimize stores through pointers */
4270 OptOnePass (0x0010, OptPtrOps);
4272 /* Optimize use of register variables */
4273 OptOnePass (0x0020, OptRegVars);
4275 /* Remove jump cascades - must be used before OptNeg */
4276 OptOnePass (0x0040, OptDoubleJumps);
4278 /* Remove unnecessary boolean negates */
4279 OptOnePass (0x0080, OptNeg);
4281 /* Replace jumps to an RTS by an RTS */
4282 OptOnePass (0x0100, OptJumpRTS);
4284 /* Optimize boolean transforms */
4285 OptOnePass (0x0200, OptBoolTransforms);
4287 /* Optimize compares */
4288 OptOnePass (0x0400, OptCompares2);
4290 /* Remove unnecessary tests */
4291 OptOnePass (0x0800, OptTests);
4293 /* Optimize several triples */
4294 OptOnePass (0x1000, OptTriples);
4296 /* Optimize basic blocks */
4297 OptOnePass (0x2000, OptBlocks);
4299 /* Remove unnecessary register loads (another pass) */
4300 OptOnePass (0x0008, OptRegLoads);
4302 /* Optimize jumps */
4303 OptOnePass (0x4000, OptJumps);
4305 /* Optimize jsr/rts sequences */
4306 OptOnePass (0x8000, OptRTS);