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 /*****************************************************************************/
51 /*****************************************************************************/
53 /*****************************************************************************/
57 /* Bitset of flags that switch the different optimizer passes */
58 unsigned long OptDisable = 0;
62 /* Bitmapped flags for the Flags field in the Line struct */
63 #define OF_CODE 0x0001 /* This line is in a code segment */
65 /* Pointer to first code line */
66 static Line* FirstCode;
69 static Line** Labels = 0; /* Pointers to label lines */
70 static unsigned LabelCount = 0; /* Count of local labels found */
72 /* A collection of lines */
73 typedef struct LineColl_ LineColl;
75 unsigned Count; /* Count of lines in the collection */
76 unsigned Max; /* Maximum count of lines */
77 Line* Lines[1]; /* Lines, dynamically allocated */
82 /* Calculate the element count of a table */
83 #define COUNT(T) (sizeof (T) / sizeof (T [0]))
85 /* Macro to increment and decrement register contents if they're valid */
86 #define INC(reg,val) if ((reg) >= 0) (reg) = ((reg) + val) & 0xFF
87 #define DEC(reg,val) if ((reg) >= 0) (reg) = ((reg) - val) & 0xFF
89 /* Defines for the conditions in a compare */
101 /* Defines for registers */
102 #define REG_NONE 0x00
106 #define REG_AX (REG_A | REG_X)
107 #define REG_ALL (REG_A | REG_X | REG_Y)
109 /* Description of the commands */
110 static const struct {
111 const char* Insn; /* Instruction */
112 unsigned char FullMatch; /* Match full instuction? */
113 unsigned char Use; /* Registers used */
114 unsigned char Load; /* Registers loaded */
116 { "\tadc\t", 0, REG_A, REG_NONE },
117 { "\tand\t", 0, REG_A, REG_NONE },
118 { "\tasl\ta", 1, REG_A, REG_NONE },
119 { "\tasl\t", 0, REG_NONE, REG_NONE },
120 { "\tclc", 1, REG_NONE, REG_NONE },
121 { "\tcld", 1, REG_NONE, REG_NONE },
122 { "\tcli", 1, REG_NONE, REG_NONE },
123 { "\tcmp\t", 0, REG_A, REG_NONE },
124 { "\tcpx\t", 0, REG_X, REG_NONE },
125 { "\tcpy\t", 0, REG_Y, REG_NONE },
126 { "\tdec\t", 0, REG_NONE, REG_NONE },
127 { "\tdex", 1, REG_X, REG_NONE },
128 { "\tdey", 1, REG_Y, REG_NONE },
129 { "\teor\t", 0, REG_A, REG_NONE },
130 { "\tinc\t", 0, REG_NONE, REG_NONE },
131 { "\tinx", 1, REG_X, REG_NONE },
132 { "\tiny", 1, REG_Y, REG_NONE },
133 { "\tjsr\tbool", 0, REG_NONE, REG_AX },
134 { "\tjsr\tdecaxy", 1, REG_ALL, REG_AX },
135 { "\tjsr\tdecax", 0, REG_AX, REG_AX },
136 { "\tjsr\tldax0sp", 1, REG_Y, REG_AX },
137 { "\tjsr\tldaxysp", 1, REG_Y, REG_AX },
138 { "\tjsr\tpusha", 1, REG_A, REG_Y },
139 { "\tjsr\tpusha0", 1, REG_A, REG_X | REG_Y },
140 { "\tjsr\tpushax", 1, REG_AX, REG_Y },
141 { "\tjsr\tpushw0sp", 1, REG_NONE, REG_ALL },
142 { "\tjsr\tpushwysp", 1, REG_Y, REG_ALL },
143 { "\tjsr\ttosicmp", 1, REG_AX, REG_ALL },
144 { "\tlda\t", 0, REG_NONE, REG_A },
145 { "\tldax\t", 0, REG_NONE, REG_AX },
146 { "\tldx\t", 0, REG_NONE, REG_X },
147 { "\tldy\t", 0, REG_NONE, REG_Y },
148 { "\tlsr\ta", 1, REG_A, REG_NONE },
149 { "\tlsr\t", 0, REG_NONE, REG_NONE },
150 { "\tnop", 1, REG_NONE, REG_NONE },
151 { "\tora\t", 0, REG_A, REG_NONE },
152 { "\tpha", 1, REG_A, REG_NONE },
153 { "\tphp", 1, REG_NONE, REG_NONE },
154 { "\tpla", 1, REG_NONE, REG_A },
155 { "\tplp", 1, REG_NONE, REG_NONE },
156 { "\trol\ta", 1, REG_A, REG_A },
157 { "\trol\t", 0, REG_NONE, REG_NONE },
158 { "\tror\ta", 1, REG_A, REG_A },
159 { "\tror\t", 0, REG_NONE, REG_NONE },
160 { "\tsbc\t", 0, REG_A, REG_NONE },
161 { "\tsec", 1, REG_NONE, REG_NONE },
162 { "\tsed", 1, REG_NONE, REG_NONE },
163 { "\tsei", 1, REG_NONE, REG_NONE },
164 { "\tsta\t", 0, REG_A, REG_NONE },
165 { "\tstx\t", 0, REG_X, REG_NONE },
166 { "\tsty\t", 0, REG_Y, REG_NONE },
167 { "\ttax", 1, REG_A, REG_X },
168 { "\ttay", 1, REG_A, REG_Y },
169 { "\ttsx", 1, REG_NONE, REG_X },
170 { "\ttxa", 1, REG_X, REG_A },
171 { "\ttya", 1, REG_Y, REG_A },
176 /* Table with the compare suffixes */
177 static const char CmpSuffixTab [][4] = {
178 "eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule"
181 /* Table used to invert a condition, indexed by condition */
182 static const unsigned char CmpInvertTab [] = {
184 CMP_LE, CMP_LT, CMP_GE, CMP_GT,
185 CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT
188 /* Table to show which compares are signed (use the N flag) */
189 static const char CmpSignedTab [] = {
190 0, 0, 1, 1, 1, 1, 0, 0, 0, 0
195 /* Lists of branches */
196 static const char* ShortBranches [] = {
207 static const char* LongBranches [] = {
221 /*****************************************************************************/
223 /*****************************************************************************/
227 static unsigned EstimateSize (Line* L);
228 /* Estimate the size of an instruction */
230 static int IsLocalLabel (const Line* L);
231 /* Return true if the line is a local label line */
233 static unsigned GetLabelNum (const char* L);
234 /* Return the label number of a label line */
236 static unsigned RVUInt1 (Line* L, LineColl* LC, unsigned Used, unsigned Unused);
237 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
241 /*****************************************************************************/
243 /*****************************************************************************/
247 static Line* NewLineAfter (Line* LineBefore, const char* Format, ...)
248 /* Create a new line, insert it after L and return it. The new line is marked
254 /* Format the new line and add it */
256 va_start (ap, Format);
257 L = NewCodeLineAfter (LineBefore, Format, ap);
260 /* Make the line a code line */
263 /* Estimate the code size */
264 L->Size = EstimateSize (L);
266 /* Return the new line */
272 static Line* NewLabelAfter (Line* L, unsigned Label)
273 /* Add a new line with a definition of a local label after the line L */
277 /* Create the label */
278 sprintf (Buf, "L%04X:", Label);
280 /* Create a new line */
281 L = NewLineAfter (L, Buf);
283 /* Insert this label into the label list */
286 /* Return the new line */
292 static void FreeLine (Line* L)
293 /* Remove a line from the list and free it */
295 /* If this is a label line, remove it from the label list */
296 if (IsLocalLabel (L)) {
297 Labels [GetLabelNum (L->Line)] = 0;
300 /* Unlink the line */
306 static Line* ReplaceLine (Line* L, const char* Format, ...)
307 /* Replace one line by another */
312 /* Format the new line */
314 va_start (ap, Format);
315 vsprintf (S, Format, ap);
318 /* Get the length of the new line */
321 /* We can copy the line if the old line has space enough */
324 /* Just copy the new line, but don't update the length */
325 memcpy (L->Line, S, Len);
326 L->Line [Len] = '\0';
330 /* We must allocate new space */
331 Line* NewLine = xmalloc (sizeof (Line) + Len);
333 /* Set the values in the new struct */
334 NewLine->Flags = L->Flags;
335 NewLine->Index = L->Index;
336 NewLine->Size = L->Size; /* Hmm ... */
338 memcpy (NewLine->Line, S, Len + 1);
340 /* Replace the old struct in the list */
341 NewLine->Next = L->Next;
343 NewLine->Next->Prev = NewLine;
348 NewLine->Prev = L->Prev;
350 NewLine->Prev->Next = NewLine;
356 /* Free the old struct */
361 /* Estimate the new size */
362 if (L->Flags & OF_CODE) {
363 L->Size = EstimateSize (L);
366 /* Return the line */
372 static Line* PrevCodeLine (Line* L)
373 /* Return the previous line containing code */
377 if (L->Flags & OF_CODE && L->Line [0] != '+') {
387 static Line* NextCodeSegLine (Line* L)
388 /* Return the next line in the code segment */
392 if (L->Flags & OF_CODE) {
402 static Line* NextCodeLine (Line* L)
403 /* Return the next line containing code */
407 if ((L->Flags & OF_CODE) != 0 && L->Line [0] != '+') {
417 static Line* NextInstruction (Line* L)
418 /* Return the next line containing code, ignoring labels. */
421 L = NextCodeLine (L);
422 } while (L && (L->Line[0] == '+' || IsLocalLabel(L)));
428 static void FreeLines (Line* Start, Line* End)
429 /* Delete all lines from Start to End, both inclusive */
434 Start = NextCodeSegLine (Start);
441 /*****************************************************************************/
442 /* Line Collections */
443 /*****************************************************************************/
447 static LineColl* NewLineColl (unsigned Size)
448 /* Create a new line collection and return it */
450 /* Allocate memory */
451 LineColl* LC = xmalloc (sizeof (LineColl) + sizeof (Line) * (Size-1));
453 /* Initialize members */
457 /* Return the new collection */
463 static void FreeLineColl (LineColl* LC)
464 /* Delete a line collection */
471 static int LCAddLine (LineColl* LC, Line* L)
472 /* Add a line. Return 0 if no space available, return 1 otherwise */
474 /* Check if there is enough space available */
475 if (LC->Count >= LC->Max) {
476 /* No room available */
481 LC->Lines [LC->Count++] = L;
489 static int LCHasLine (LineColl* LC, Line* L)
490 /* Check if the given line is in the collection */
493 for (I = 0; I < LC->Count; ++I) {
494 if (LC->Lines[I] == L) {
503 /*****************************************************************************/
504 /* Test a line for several things */
505 /*****************************************************************************/
509 static int IsLocalLabel (const Line* L)
510 /* Return true if the line is a local label line */
512 return (L->Line [0] == 'L' && isxdigit (L->Line [1]));
517 static int IsLabel (const Line* L)
518 /* Return true if the line is a label line */
520 return (L->Line [0] == 'L' && isxdigit (L->Line [1])) ||
521 (L->Line [0] == '_');;
526 static int IsHintLine (const Line* L)
527 /* Return true if the line contains an optimizer hint */
529 return L->Line [0] == '+';
534 static int IsSegHint (const Line* L)
535 /* Return true if the given line contains a segment hint */
537 return (L->Line [0] == '+' && strncmp (L->Line + 1, "seg:", 4) == 0);
542 static int IsHint (const Line* L, const char* Hint)
543 /* Check if the line contains a given hint */
545 return (L->Line [0] == '+' && strcmp (L->Line + 1, Hint) == 0);
550 static int IsCondJump (Line* L)
551 /* Return true if the line contains a conditional jump */
553 return (L->Line [0] == '\t' &&
554 (strncmp (L->Line + 1, "beq\t", 4) == 0 ||
555 strncmp (L->Line + 1, "bne\t", 4) == 0 ||
556 strncmp (L->Line + 1, "jeq\t", 4) == 0 ||
557 strncmp (L->Line + 1, "jne\t", 4) == 0));
562 static int IsXIndAddrMode (Line* L)
563 /* Return true if the given line does use the X register */
565 unsigned Len = strlen (L->Line);
566 return (strcmp (L->Line + Len - 3, ",x)") == 0 ||
567 strcmp (L->Line + Len - 2, ",x") == 0);
572 static int NoXIndAddrMode (Line* L)
573 /* Return true if the given line does use the X register */
575 return !IsXIndAddrMode (L);
580 static int IsYIndAddrMode (Line* L)
581 /* Return true if the given line does use the Y register */
583 unsigned Len = strlen (L->Line);
584 return (strcmp (L->Line + Len - 2, ",y") == 0);
589 static Line* FindHint (Line* L, const char* Hint)
590 /* Search for a line with the given hint */
593 if (IsHint (L, Hint)) {
603 static unsigned GetHexNum (const char* S)
604 /* Get a hex number from a string */
608 while (isxdigit (S [I])) {
609 int C = (unsigned char) (S [I++]);
615 Val = (Val << 4) + C;
622 static unsigned GetLabelNum (const char* L)
623 /* Return the label number of a label line */
626 return GetHexNum (L+1);
631 static Line* GetTargetLine (const char* L)
632 /* Get the line with the target label of a jump. L must be a pointer to the
633 * string containing the label number.
638 /* Get the label number of the target */
639 unsigned Label = GetLabelNum (L);
640 CHECK (Label < LabelCount);
642 /* Get the line with this label */
643 Target = Labels [Label];
644 CHECK (Target != 0 && (Target->Flags & OF_CODE) != 0);
652 static unsigned GetJumpDistance (Line* L, Line* Target)
653 /* Get the distance between both lines */
655 unsigned Distance = 0;
658 if (Target->Index > L->Index) {
659 /* This is a forward jump. */
661 L = NextCodeLine (L);
663 } while (L != Target);
665 /* This is a backward jump */
667 L = PrevCodeLine (L);
669 } while (L != Target);
673 /* Return the calculated distance */
679 static int LineMatch (const Line* L, const char* Start)
680 /* Check if the start of the line matches Start */
682 return strncmp (L->Line, Start, strlen (Start)) == 0;
687 static int LineFullMatch (const Line* L, const char* Start)
688 /* Check if the matches Start */
690 return strcmp (L->Line, Start) == 0;
695 static int LineMatchX (const Line* L, const char** Start)
696 /* Check the start of the line against a list of patterns. Return the
697 * number of the pattern that matched, or -1 in case of no match.
702 if (LineMatch (L, *Start)) {
715 static int LineFullMatchX (const Line* L, const char** Start)
716 /* Check the the line against a list of patterns. Return the
717 * number of the pattern that matched, or -1 in case of no match.
722 if (LineFullMatch (L, *Start)) {
735 static int IsLoadAX (Line* L1, Line* L2)
736 /* Check if the both lines load a static variable into ax. That is, both lines
742 return LineMatch (L1, "\tlda\t") &&
743 LineMatch (L2, "\tldx\t") &&
744 strncmp (L1->Line+5, L2->Line+5, strlen (L1->Line+5)) == 0 &&
745 strcmp (L2->Line+strlen(L1->Line), "+1") == 0;
750 /*****************************************************************************/
751 /* Initial optimizer setup */
752 /*****************************************************************************/
756 static void FindCodeStart (void)
757 /* Find and remember the first line of actual code */
759 Line* L = FindHint (FirstLine, "end_of_preamble");
760 FirstCode = L? L->Next : 0;
765 static unsigned EstimateDataSize (Line* L, unsigned Chunk)
766 /* Estimate the size of a .byte, .word or .dword command */
768 unsigned Size = Chunk;
770 while ((S = strchr (S, ',')) != 0) {
779 static unsigned EstimateSize (Line* L)
780 /* Estimate the size of an instruction */
782 static const char* Transfers [] = {
792 if (L->Line [0] != '\t') {
795 if (LineMatch (L, "\tldax\t")) {
796 /* Immidiate load of both, A and X */
799 if (LineMatch (L, "\tld")) {
800 OpStart = L->Line [5];
801 return (OpStart == '#' || OpStart == '(')? 2 : 3;
803 if (LineMatch (L, "\tst")) {
804 OpStart = L->Line [5];
805 return (OpStart == '(')? 2 : 3;
807 if (LineMatch (L, "\t.byte\t")) {
808 return EstimateDataSize (L, 1);
810 if (LineMatch (L, "\t.word\t")) {
811 return EstimateDataSize (L, 2);
813 if (LineMatch (L, "\t.dword\t")) {
814 return EstimateDataSize (L, 4);
816 if (LineMatchX (L, ShortBranches) >= 0) {
819 if (LineMatchX (L, LongBranches) >= 0) {
822 if (LineMatchX (L, Transfers) >= 0) {
830 static void MarkCodeLines (void)
831 /* Mark all lines that are inside a code segment */
837 InCode = IsHint (L, "seg:code");
838 } else if (InCode && L->Line[0] != '\0') {
840 L->Size = EstimateSize (L);
848 static void CreateLabelList (void)
849 /* Create a list with pointers to local labels */
855 /* Get the next label number. This is also the current label count.
856 * Make some room for more labels when optimizing code.
858 LabelCount = GetLabel () + 100;
860 /* Allocate memory for the array and clear it */
861 Labels = xmalloc (LabelCount * sizeof (Line*));
862 for (I = 0; I < LabelCount; ++I) {
866 /* Walk through the code and insert all label lines */
869 if (IsLocalLabel (L)) {
870 unsigned LabelNum = GetLabelNum (L->Line);
871 CHECK (LabelNum < LabelCount);
872 Labels [LabelNum] = L;
880 static unsigned AllocLabel (void)
881 /* Get a new label. The current code does not realloc the label list, so there
882 * must be room enough in the current list.
887 /* Search for a free slot, start at 1, since 0 is "no label" */
888 for (I = 1; I < LabelCount; ++I) {
889 if (Labels[I] == 0) {
890 /* Found a free slot */
895 /* No label space available */
896 Internal ("Out of label space in the optimizer");
904 /*****************************************************************************/
905 /* Helper functions */
906 /*****************************************************************************/
910 static int GetNextCodeLines (Line* L, Line** Lines, unsigned Count)
911 /* Get a number of code lines ignoring hints and other stuff. The function
912 * returns 1 if we got the lines and 0 if we are at the end of the code
913 * segment or if we hit a label.
918 /* Get the next valid line */
920 L = NextCodeLine (L);
921 } while (L && IsHintLine (L));
923 /* Did we get one? */
924 if (L == 0 || IsLabel (L)) {
929 /* Remember the line */
939 static int FindCond (const char* Suffix)
940 /* Map a condition suffix to a code. Return the code or -1 on failure */
945 for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) {
946 if (strncmp (Suffix, CmpSuffixTab [I], strlen (CmpSuffixTab[I])) == 0) {
958 static int CheckAndGetIntCmp (const Line* JSR, const Line* JMP)
959 /* Helper function to check for a compare subroutine call followed by a
960 * conditional branch. Will return the condition found, or -1 if no
961 * or invalid condition.
968 /* Extract the condition from the function name. */
969 if ((Cond [0] = JSR->Line [8]) == 'u') {
970 Cond [1] = JSR->Line [9];
971 Cond [2] = JSR->Line [10];
973 Tail = JSR->Line + 11;
975 Cond [1] = JSR->Line [9];
977 Tail = JSR->Line + 10;
980 /* Check if this is indeed an integer function */
981 if (strcmp (Tail, "ax") != 0) {
986 /* Get the condition code */
993 /* Invert the code if we jump on condition not met. */
994 if (JMP->Line [2] == 'e' && JMP->Line [3] == 'q') {
995 /* Jumps if condition false, invert condition */
996 C = CmpInvertTab [C];
999 /* Return the condition code */
1005 static int TosCmpFunc (Line* L)
1006 /* Check if this is a call to one of the TOS compare functions (tosgtax).
1007 * Return the condition code or -1 if not found.
1010 if (LineMatch (L, "\tjsr\ttos") &&
1011 strcmp (L->Line+strlen(L->Line)-2, "ax") == 0) {
1013 /* Ok, found. Get the condition. */
1014 return FindCond (L->Line+8);
1025 static int IsUnsignedCmp (int Code)
1026 /* Check if this is an unsigned compare */
1029 return CmpSignedTab [Code] == 0;
1034 static void InvertZJump (Line* L)
1035 /* Invert a jeq/jne jump */
1037 if (L->Line [2] == 'n' && L->Line [3] == 'e') {
1038 /* This was a bne/jne */
1042 /* This was (hopefully) a beq/jeq */
1050 static int FindCmd (Line* L)
1054 /* Search for the known patterns */
1055 for (I = 0; I < COUNT(CmdDesc); ++I) {
1056 if (CmdDesc[I].FullMatch) {
1057 if (LineFullMatch (L, CmdDesc[I].Insn)) {
1062 if (LineMatch (L, CmdDesc[I].Insn)) {
1074 static unsigned RVUInt2 (Line* L,
1075 LineColl* LC, /* To remember visited lines */
1076 unsigned Used, /* Definitely used registers */
1077 unsigned Unused) /* Definitely unused registers */
1078 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
1082 /* Check the following instructions. We classifiy them into primary
1083 * loads (register value not used), neutral (check next instruction),
1084 * and unknown (assume register was used).
1090 /* Get the next line and follow jumps */
1093 /* Handle jumps to local labels (continue there) */
1094 if (LineMatch (L, "\tjmp\tL")) {
1095 /* Get the target of the jump */
1096 L = GetTargetLine (L->Line+5);
1099 /* Get the next instruction line */
1100 L = NextInstruction (L);
1102 /* Bail out if we're done */
1103 if (L == 0 || IsLabel (L)) {
1104 /* Something is wrong */
1108 /* Check if we had this line already. If so, bail out, if not,
1109 * add it to the list of known lines.
1111 if (LCHasLine (LC, L) || !LCAddLine (LC, L)) {
1115 } while (LineMatch (L, "\tjmp\tL"));
1117 /* Special handling for branches */
1118 if (LineMatchX (L, ShortBranches) >= 0 ||
1119 LineMatchX (L, LongBranches) >= 0) {
1120 const char* Target = L->Line+5;
1121 if (Target[0] == 'L') {
1122 /* Jump to local label. Check the register usage starting at
1123 * the branch target and at the code following the branch.
1124 * All registers that are unused in both execution flows are
1125 * returned as unused.
1128 U2 = RVUInt1 (GetTargetLine (Target), LC, Used, Unused);
1129 U1 = RVUInt1 (L, LC, Used, Unused);
1130 return U1 | U2; /* Used in any of the branches */
1134 /* Search for the instruction in this line */
1137 /* If we don't find it, assume all other registers are */
1142 /* Evaluate the use flags, check for addressing modes */
1144 if (IsXIndAddrMode (L)) {
1146 } else if (IsYIndAddrMode (L)) {
1150 /* Remove registers that were already new loaded */
1153 /* Remember the remaining registers */
1157 /* Evaluate the load flags */
1158 R = CmdDesc[I].Load;
1160 /* Remove registers that were already used */
1163 /* Remember the remaining registers */
1167 /* If we know about all registers, bail out */
1168 if ((Used | Unused) == REG_ALL) {
1174 /* Return to the caller the complement of all unused registers */
1175 return ~Unused & REG_ALL;
1180 static unsigned RVUInt1 (Line* L,
1181 LineColl* LC, /* To remember visited lines */
1182 unsigned Used, /* Definitely used registers */
1183 unsigned Unused) /* Definitely unused registers */
1184 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
1186 /* Remember the current count of the line collection */
1187 unsigned Count = LC->Count;
1189 /* Call the worker routine */
1190 unsigned R = RVUInt2 (L, LC, Used, Unused);
1192 /* Restore the old count */
1195 /* Return the result */
1201 static unsigned RegValUsed (Line* Start)
1202 /* Check the next instructions after the one in L for register usage. If
1203 * a register is used as an index, or in a store or other instruction, it
1204 * is assumed to be used. If a register is loaded with a value, before it
1205 * was used by one of the actions described above, it is assumed unused.
1206 * If the end of the lookahead is reached, all registers that are uncertain
1207 * are marked as used.
1208 * The result of the search is returned.
1213 /* Create a new line collection and enter the start line */
1214 LineColl* LC = NewLineColl (256);
1215 LCAddLine (LC, Start);
1217 /* Call the recursive subfunction */
1218 R = RVUInt1 (Start, LC, REG_NONE, REG_NONE);
1220 /* Delete the line collection */
1223 /* Return the registers used */
1229 static int RegAUsed (Line* Start)
1230 /* Check if the value in A is used. */
1232 return (RegValUsed (Start) & REG_A) != 0;
1237 static int RegXUsed (Line* Start)
1238 /* Check if the value in X is used. */
1240 return (RegValUsed (Start) & REG_X) != 0;
1245 static int RegYUsed (Line* Start)
1246 /* Check if the value in Y is used. */
1248 return (RegValUsed (Start) & REG_Y) != 0;
1253 /*****************************************************************************/
1254 /* Real optimizer routines */
1255 /*****************************************************************************/
1259 static void OptCompares1 (void)
1260 /* Try to optimize the integer compare subroutines. */
1262 Line* L2[10]; /* Line lookahead */
1263 int Cond; /* Condition to evaluate */
1264 unsigned Label; /* Local label number */
1265 unsigned Offs; /* Stack offset */
1266 Line* DelStart; /* First line to delete */
1268 Line* L = FirstCode;
1271 /* Search for compares of local byte sized variables. This looks
1283 * Replace it by a direct compare:
1291 if (LineMatch (L, "\tldy\t#$") &&
1292 GetNextCodeLines (L, L2, 7) &&
1293 LineFullMatch (L2[0], "\tldx\t#$00") &&
1294 LineFullMatch (L2[1], "\tlda\t(sp),y") &&
1295 LineFullMatch (L2[2], "\tjsr\tpushax") &&
1296 LineMatch (L2[3], "\tldy\t#$") &&
1297 LineFullMatch (L2[4], "\tldx\t#$00") &&
1298 LineFullMatch (L2[5], "\tlda\t(sp),y") &&
1299 (Cond = TosCmpFunc (L2[6])) >= 0) {
1301 /* Get the stack offset and correct it, since we will remove
1304 Offs = GetHexNum (L2[3]->Line+7) - 2;
1307 L = NewLineAfter (L, "\tlda\t(sp),y");
1308 L = NewLineAfter (L, "\tldy\t#$%02X", Offs);
1309 L = NewLineAfter (L, "\tcmp\t(sp),y");
1310 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1312 /* Remove the old cruft */
1313 FreeLines (L2[0], L2[6]);
1316 /* Compares of byte sized global variables */
1317 else if (LineFullMatch (L, "\tldx\t#$00") &&
1318 GetNextCodeLines (L, L2, 5) &&
1319 LineMatch (L2[0], "\tlda\t") &&
1320 LineFullMatch (L2[1], "\tjsr\tpushax") &&
1321 LineFullMatch (L2[2], "\tldx\t#$00") &&
1322 LineMatch (L2[3], "\tlda\t") &&
1323 (Cond = TosCmpFunc (L2[4])) >= 0) {
1326 if (IsXIndAddrMode (L2[0])) {
1327 /* The load is X indirect, so we may not remove the load
1328 * of the X register.
1333 L = ReplaceLine (L, L2[0]->Line);
1336 L = NewLineAfter (L, "\tcmp\t%s", L2[3]->Line+5);
1337 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1339 /* Remove the old cruft */
1340 FreeLines (DelStart, L2[4]);
1344 /* Byte sized local to global */
1345 else if (LineMatch (L, "\tldy\t#$") &&
1346 GetNextCodeLines (L, L2, 6) &&
1347 LineFullMatch (L2[0], "\tldx\t#$00") &&
1348 LineFullMatch (L2[1], "\tlda\t(sp),y") &&
1349 LineFullMatch (L2[2], "\tjsr\tpushax") &&
1350 LineFullMatch (L2[3], "\tldx\t#$00") &&
1351 LineMatch (L2[4], "\tlda\t") &&
1352 (Cond = TosCmpFunc (L2[5])) >= 0) {
1355 L = NewLineAfter (L, L2[1]->Line);
1356 L = NewLineAfter (L, "\tcmp\t%s", L2[4]->Line+5);
1357 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1359 /* Remove the old cruft */
1360 FreeLines (L2[0], L2[5]);
1364 /* Byte sized global to local */
1365 else if (LineFullMatch (L, "\tldx\t#$00") &&
1366 GetNextCodeLines (L, L2, 6) &&
1367 LineMatch (L2[0], "\tlda\t") &&
1368 LineFullMatch (L2[1], "\tjsr\tpushax") &&
1369 LineMatch (L2[2], "\tldy\t#$") &&
1370 LineFullMatch (L2[3], "\tldx\t#$00") &&
1371 LineFullMatch (L2[4], "\tlda\t(sp),y") &&
1372 (Cond = TosCmpFunc (L2[5])) >= 0) {
1374 /* Get the stack offset and correct it, since we will remove
1377 Offs = GetHexNum (L2[2]->Line+7) - 2;
1380 if (IsXIndAddrMode (L2[0])) {
1381 /* The load is X indirect, so we may not remove the load
1382 * of the X register.
1387 L = ReplaceLine (L, L2[0]->Line);
1390 L = NewLineAfter (L, "\tldy\t#$%02X", Offs);
1391 L = NewLineAfter (L, "\tcmp\t(sp),y");
1392 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1394 /* Remove the old cruft */
1395 FreeLines (DelStart, L2[5]);
1399 /* Search for unsigned compares against global variables. This looks
1407 * Replace that by a direct compare:
1415 else if (LineFullMatch (L, "\tjsr\tpushax") &&
1416 GetNextCodeLines (L, L2, 3) &&
1417 IsLoadAX (L2[0], L2[1]) &&
1418 (Cond = TosCmpFunc (L2[2])) >= 0 &&
1419 IsUnsignedCmp (Cond)) {
1421 /* Get a free label number */
1422 Label = AllocLabel ();
1424 /* Replace the code */
1425 L = ReplaceLine (L, "\tcpx\t%s", L2[1]->Line+5);
1426 L = NewLineAfter (L, "\tbne\tL%04X", Label);
1427 L = NewLineAfter (L, "\tcmp\t%s", L2[0]->Line+5);
1428 L = NewLabelAfter(L, Label);
1429 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1431 /* Remove the old code */
1432 FreeLines (L2[0], L2[2]);
1436 L = NextCodeLine (L);
1442 static void OptDeadJumps (void)
1443 /* Remove jumps to the following instruction */
1445 static const char* Jumps [] = {
1454 Line* L = FirstCode;
1457 /* Get a pointer to the next instruction line */
1458 Line* NextLine = NextInstruction (L);
1460 /* Is this line a jump? */
1461 int I = LineMatchX (L, Jumps);
1463 /* Yes. Get the target label, skip labels */
1464 Line* Target = NextInstruction (GetTargetLine (L->Line+5));
1466 /* If the target label is the next line, remove the jump */
1467 if (Target == NextLine) {
1472 /* Go to the next line */
1479 static void OptLoads (void)
1480 /* Remove unnecessary loads of values */
1484 Line* L = FirstCode;
1505 * This change will cost 3 cycles (one additional jump inside the
1506 * subroutine), but it saves a lot of code (6 bytes per occurrence),
1507 * so we will accept the overhead. It may even be possible to rewrite
1508 * the library routine to get rid of the additional overhead.
1510 if (LineMatch (L, "\tldy\t#$") &&
1511 GetNextCodeLines (L, L2, 5) &&
1512 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
1513 LineFullMatch (L2 [1], "\ttax") &&
1514 LineFullMatch (L2 [2], "\tdey") &&
1515 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
1516 LineFullMatch (L2 [4], "\tjsr\tpushax")) {
1518 /* Found - replace it */
1519 if (LineFullMatch (L, "\tldy\t#$01")) {
1520 /* Word at offset zero */
1522 L = ReplaceLine (L2 [4], "\tjsr\tpushw0sp");
1524 ReplaceLine (L2 [4], "\tjsr\tpushwysp");
1527 /* Delete the remaining lines */
1528 FreeLines (L2 [0], L2 [3]);
1551 * This change will cost 2 cycles, but it saves a lot of code (6 bytes
1552 * per occurrence), so we will accept the overhead. It may even be
1553 * possible to rewrite the library routine to get rid of the additional
1556 if (LineMatch (L, "\tldy\t#$") &&
1557 GetNextCodeLines (L, L2, 6) &&
1558 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
1559 LineFullMatch (L2 [1], "\ttax") &&
1560 LineFullMatch (L2 [2], "\tdey") &&
1561 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
1562 LineMatch (L2 [4], "\tldy\t#$") &&
1563 LineFullMatch (L2 [5], "\tjsr\tldauidx")) {
1565 /* Found - replace it */
1566 L2 [4]->Line [3] = 'x'; /* Change to ldx */
1567 if (LineFullMatch (L, "\tldy\t#$01")) {
1568 /* Word at offset zero */
1570 L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp");
1572 ReplaceLine (L2 [5], "\tjsr\tldauiysp");
1575 /* Delete the remaining lines */
1576 FreeLines (L2 [0], L2 [3]);
1588 if (LineFullMatch (L, "\tlda\t(sp),y") &&
1589 GetNextCodeLines (L, L2, 1) &&
1590 LineFullMatch (L2 [0], "\tjsr\tpusha")) {
1592 /* Found, replace it */
1593 L = ReplaceLine (L, "\tjsr\tpushaysp");
1597 /* All other patterns start with this one: */
1598 if (!LineFullMatch (L, "\tldx\t#$00")) {
1608 * and replace it by:
1613 if (GetNextCodeLines (L, L2, 1) &&
1614 LineFullMatch (L2 [0], "\tjsr\tpushax")) {
1616 /* Replace the subroutine call */
1617 L = ReplaceLine (L, "\tjsr\tpusha0");
1619 /* Remove the unnecessary line */
1629 * and replace it by:
1635 else if (GetNextCodeLines (L, L2, 2) &&
1636 LineMatch (L2 [0], "\tlda\t") &&
1637 LineFullMatch (L2 [1], "\tjsr\tpushax")) {
1639 /* Be sure, X is not used in the load */
1640 if (NoXIndAddrMode (L2 [0])) {
1642 /* Replace the subroutine call */
1643 L2 [1] = ReplaceLine (L2 [1], "\tjsr\tpusha0");
1645 /* Remove the unnecessary load */
1648 /* L must be valid */
1660 * and replace it by:
1665 else if (GetNextCodeLines (L, L2, 2) &&
1666 LineMatch (L2 [0], "\tlda\t") &&
1667 LineMatch (L2 [1], "\tcmp\t#$")) {
1669 /* Be sure, X is not used in the load */
1670 if (NoXIndAddrMode (L2 [0])) {
1672 /* Remove the unnecessary load */
1675 /* L must be valid */
1686 * and replace it by:
1691 else if (GetNextCodeLines (L, L2, 2) &&
1692 LineMatch (L2 [0], "\tlda\t") &&
1693 LineFullMatch (L2 [1], "\tjsr\tbnega")) {
1695 /* Be sure, X is not used in the load */
1696 if (NoXIndAddrMode (L2 [0])) {
1698 /* Remove the unnecessary load */
1701 /* L must be valid */
1707 /* Go to the next line */
1708 L = NextCodeLine (L);
1714 static void OptRegLoads (void)
1715 /* Remove unnecessary loads of registers */
1721 /* Repeat this until there is nothing more to delete */
1729 /* Search for a load of X and check if the value is used later */
1730 if (LineMatch (L, "\tldx\t") &&
1732 !IsCondJump (NextInstruction (L))) {
1734 /* Remember to delete this line */
1738 /* Search for a load of A and check if the value is used later */
1739 else if (LineMatch (L, "\tlda\t") &&
1741 !IsCondJump (NextInstruction (L))) {
1743 /* Remember to delete this line */
1747 /* Search for a load of Y and check if the value is used later */
1748 else if (LineMatch (L, "\tldy\t") &&
1750 !IsCondJump (NextInstruction (L))) {
1752 /* Remember to delete this line */
1756 /* Go to the next line, delete the current if requested */
1758 L = NextCodeLine (L);
1764 } while (Deletions > 0);
1769 static int OptPtrOps1 (Line** Start)
1770 /* Optimize several pointer and array constructs - subfunction 1 */
1775 unsigned LinesToRemove;
1780 /* Use a local variable for the working line */
1783 /* Search for (23B/XXT)
1795 * and replace it by something like (24B/26T)
1809 if (!LineMatch (L, "\tlda\t") ||
1810 !GetNextCodeLines (L, L2, 4) ||
1811 !IsLoadAX (L, L2 [0]) ||
1812 !LineFullMatch (L2[1], "\tsta\tregsave") ||
1813 !LineFullMatch (L2[2], "\tstx\tregsave+1")) {
1820 if (LineMatch (L2[3], "\tjsr\tincax")) {
1821 /* Get next code lines */
1822 if (GetNextCodeLines (L2[3], &L2[4], 4) == 0) {
1823 /* Cannot get lines */
1826 Inc = GetHexNum (L2[3]->Line+10);
1830 /* Get next code lines */
1831 if (GetNextCodeLines (L2[3], &L2[4], 7) == 0) {
1832 /* Cannot get lines */
1835 if (LineFullMatch (L2[3], "\tclc") &&
1836 LineMatch (L2[4], "\tadc\t#$") &&
1837 LineFullMatch (L2[5], "\tbcc\t*+3") &&
1838 LineFullMatch (L2[6], "\tinx")) {
1839 /* Inlined increment */
1840 Inc = GetHexNum (L2[4]->Line+7);
1849 /* Check for the remainder */
1850 if (!LineMatch (L3[0], "\tsta\t") ||
1851 strcmp (L3[0]->Line+5, L->Line+5) != 0 ||
1852 !LineMatch (L3[1], "\tstx\t") ||
1853 strcmp (L3[1]->Line+5, L2[0]->Line+5) != 0 ||
1854 !LineFullMatch (L3[2], "\tlda\tregsave") ||
1855 !LineFullMatch (L3[3], "\tldx\tregsave+1")) {
1861 /* Check if AX is actually used following the code above. If not,
1862 * we don't need to load A/X from regsave. Since X will never by
1863 * used without A, check just for A.
1866 if (!RegAUsed (L3[3])) {
1867 /* We don't need to load regsave */
1871 /* Special code for register variables */
1873 if (LineMatch (L, "\tlda\tregbank+") &&
1874 GetNextCodeLines (L3[3], &L3[4], 1) &&
1877 /* Remember the offset into the register bank */
1879 strcpy (Reg, L->Line+5);
1881 /* Check for several special sequences */
1882 if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
1883 /* Load char indirect */
1884 L = ReplaceLine (L, "\tldx\t#$00");
1885 L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
1886 L = NewLineAfter (L, "\tinc\t%s", Reg);
1887 L = NewLineAfter (L, "\tbne\t*+4");
1888 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1891 } else if (LineFullMatch (L3[4], "\tsta\tptr1") &&
1892 GetNextCodeLines (L3[4], &L3[5], 3) &&
1893 LineFullMatch (L3[5], "\tstx\tptr1+1") &&
1894 LineFullMatch (L3[6], "\tldx\t#$00") &&
1895 LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
1897 /* Load char indirect, inlined */
1898 L = ReplaceLine (L, "\tldx\t#$00");
1899 L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
1900 L = NewLineAfter (L, "\tinc\t%s", Reg);
1901 L = NewLineAfter (L, "\tbne\t*+4");
1902 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1906 } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
1907 if (GetNextCodeLines (L3[4], &L3[5], 2) &&
1908 LineMatch (L3[5], "\tlda\t") &&
1909 LineFullMatch (L3[6], "\tjsr\tstaspp")) {
1911 /* Store to pointer */
1912 L = ReplaceLine (L, L3[5]->Line);
1913 L = NewLineAfter (L, "\tldy\t#$00");
1914 L = NewLineAfter (L, "\tsta\t(%s),y", Reg);
1915 L = NewLineAfter (L, "\tinc\t%s", Reg);
1916 L = NewLineAfter (L, "\tbne\t*+4");
1917 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1922 } else if (GetNextCodeLines (L3[4], &L3[5], 3) &&
1923 LineMatch (L3[5], "\tldy\t#$") &&
1924 LineFullMatch (L3[6], "\tlda\t(sp),y") &&
1925 LineFullMatch (L3[7], "\tjsr\tstaspp")) {
1927 /* Beware: We have to correct the stack offset, since we will
1928 * remove the pushax instruction!
1930 Offs = GetHexNum (L3[5]->Line+7) - 2;
1932 /* Store to pointer */
1933 L = ReplaceLine (L, "\tldy\t#$%02X", Offs);
1934 L = NewLineAfter (L, "\tldx\t#$00");
1935 L = NewLineAfter (L, "\tlda\t(sp),y");
1936 L = NewLineAfter (L, "\tsta\t(%s,x)", Reg);
1937 L = NewLineAfter (L, "\tinc\t%s", Reg);
1938 L = NewLineAfter (L, "\tbne\t*+4");
1939 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1949 /* No register variable - insert the first part of the code */
1951 L = NewLineAfter (L, "\tsta\tptr1");
1953 L = NewLineAfter (L, "\tclc");
1954 L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
1955 L = NewLineAfter (L, "\tsta\t%s", L3[0]->Line+5);
1956 L = NewLineAfter (L, "\tlda\t%s", L3[1]->Line+5);
1958 L = NewLineAfter (L, "\tsta\tptr1+1");
1960 L = NewLineAfter (L, "\tadc\t#$00");
1961 L = NewLineAfter (L, "\tsta\t%s", L3[1]->Line+5);
1963 /* Check if we must really load the old value into a/x or if the
1964 * code may be replaced by something else.
1966 if (GetNextCodeLines (L3[3], &L3[4], 1)) {
1967 if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
1968 /* Load char indirect */
1969 L = NewLineAfter (L, "\tldx\t#$00");
1970 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
1973 } else if (LineFullMatch (L3[4], "\tsta\tptr1") &&
1974 GetNextCodeLines (L3[4], &L3[5], 3) &&
1975 LineFullMatch (L3[5], "\tstx\tptr1+1") &&
1976 LineFullMatch (L3[6], "\tldx\t#$00") &&
1977 LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
1979 /* Load char indirect, inlined */
1980 L = NewLineAfter (L, "\tldx\t#$00");
1981 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
1985 } else if (LineFullMatch (L3[4], "\tjsr\tldaxi")) {
1986 /* Load word indirect */
1987 L = NewLineAfter (L, "\tldy\t#$01");
1988 L = NewLineAfter (L, "\tlda\t(ptr1),y");
1989 L = NewLineAfter (L, "\ttax");
1990 L = NewLineAfter (L, "\tdey");
1991 L = NewLineAfter (L, "\tlda\t(ptr1),y");
1995 } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
1996 if (GetNextCodeLines (L3[4], &L3[5], 2) &&
1997 LineMatch (L3[5], "\tlda\t") &&
1998 LineFullMatch (L3[6], "\tjsr\tstaspp")) {
2000 /* Store to pointer */
2001 L = NewLineAfter (L, L3[5]->Line);
2002 L = NewLineAfter (L, "\tldy\t#$00");
2003 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2007 } else if (GetNextCodeLines (L3[4], &L3[5], 3) &&
2008 LineMatch (L3[5], "\tldy\t#$") &&
2009 LineFullMatch (L3[6], "\tlda\t(sp),y") &&
2010 LineFullMatch (L3[7], "\tjsr\tstaspp")) {
2012 /* Beware: We have to correct the stack offset, since we will
2013 * remove the pushax instruction!
2015 sprintf (L3[5]->Line+7, "%02X", GetHexNum (L3[5]->Line+7)-2);
2017 /* Store to pointer */
2018 L = NewLineAfter (L, L3[5]->Line);
2019 L = NewLineAfter (L, L3[6]->Line);
2020 L = NewLineAfter (L, "\tldy\t#$00");
2021 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2030 /* If we need to load a/x, add the code */
2032 L = NewLineAfter (L, "\ttax");
2033 L = NewLineAfter (L, "\tlda\tptr1");
2037 /* Remove the code that is no longer needed */
2038 FreeLines (L2[0], L2[LinesToRemove-1]);
2040 /* Return the new line and success */
2041 *Start = NextCodeLine (L);
2047 static int OptPtrOps2 (Line** Start)
2048 /* Optimize several pointer and array constructs - subfunction 2 */
2053 unsigned LinesToRemove;
2058 /* Use a local variable for the working line */
2061 /* Same as subfunction 1 but for local variables. */
2062 if (LineMatch (L, "\tldy\t#$") == 0) {
2066 /* Get the stack offset. The offset points to the high byte, correct that. */
2067 Offs = GetHexNum (L->Line+7) - 1;
2069 /* Check for the actual sequences */
2070 if (GetNextCodeLines (L, L2, 7) &&
2071 LineFullMatch (L2[0], "\tjsr\tldaxysp") &&
2072 LineFullMatch (L2[1], "\tsta\tregsave") &&
2073 LineFullMatch (L2[2], "\tstx\tregsave+1") &&
2074 LineMatch (L2[3], "\tjsr\tincax")) {
2076 /* Non inlined version */
2077 Inc = GetHexNum (L2[3]->Line+10);
2079 /* Check for stack offset zero */
2080 if (LineFullMatch (L2[4], "\tjsr\tstax0sp") &&
2081 LineFullMatch (L2[5], "\tlda\tregsave") &&
2082 LineFullMatch (L2[6], "\tldx\tregsave+1")) {
2086 } else if (GetNextCodeLines (L2[6], &L2[7], 1) &&
2087 LineMatch (L2[4], "\tldy\t#$") &&
2088 GetHexNum (L2[4]->Line+7) == Offs &&
2089 LineFullMatch (L2[5], "\tjsr\tstaxysp") &&
2090 LineFullMatch (L2[6], "\tlda\tregsave") &&
2091 LineFullMatch (L2[7], "\tldx\tregsave+1")) {
2100 } else if (GetNextCodeLines (L, L2, 13) &&
2101 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
2102 LineFullMatch (L2[1], "\ttax") &&
2103 LineFullMatch (L2[2], "\tdey") &&
2104 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
2105 LineFullMatch (L2[4], "\tsta\tregsave") &&
2106 LineFullMatch (L2[5], "\tstx\tregsave+1") &&
2107 LineFullMatch (L2[6], "\tclc") &&
2108 LineMatch (L2[7], "\tadc\t#$") &&
2109 LineFullMatch (L2[8], "\tbcc\t*+3") &&
2110 LineFullMatch (L2[9], "\tinx")) {
2112 /* Inlined version */
2113 Inc = GetHexNum (L2[7]->Line+7);
2115 /* Check for stack offset zero */
2116 if (LineFullMatch (L2[10], "\tjsr\tstax0sp") &&
2117 LineFullMatch (L2[11], "\tlda\tregsave") &&
2118 LineFullMatch (L2[12], "\tldx\tregsave+1")) {
2122 } else if (GetNextCodeLines (L2[12], &L2[13], 1) &&
2123 LineMatch (L2[10], "\tldy\t#$") &&
2124 GetHexNum (L2[10]->Line+7) == Offs &&
2125 LineFullMatch (L2[11], "\tjsr\tstaxysp") &&
2126 LineFullMatch (L2[12], "\tlda\tregsave") &&
2127 LineFullMatch (L2[13], "\tldx\tregsave+1")) {
2140 /* Get a pointer to the last line of the preceding sequence */
2141 L3 = &L2[LinesToRemove-1];
2143 /* Check if AX is actually used following the code above. If not,
2144 * we don't need to load A/X from regsave. Since X will never by
2145 * used without A, check just for A.
2148 if (!RegAUsed (L3[0])) {
2149 /* We don't need to load regsave */
2153 /* Replace the ldy instruction, offset must point to the low byte */
2154 sprintf (L->Line+7, "%02X", Offs);
2156 /* Insert the first part of the code */
2157 L = NewLineAfter (L, "\tlda\t(sp),y");
2159 L = NewLineAfter (L, "\tsta\tptr1");
2161 L = NewLineAfter (L, "\tclc");
2162 L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
2163 L = NewLineAfter (L, "\tsta\t(sp),y");
2164 L = NewLineAfter (L, "\tiny");
2165 L = NewLineAfter (L, "\tlda\t(sp),y");
2167 L = NewLineAfter (L, "\tsta\tptr1+1");
2169 L = NewLineAfter (L, "\tadc\t#$00");
2170 L = NewLineAfter (L, "\tsta\t(sp),y");
2172 /* Check if we must really load the old value into a/x or if the
2173 * code may be replaced by something else.
2175 if (GetNextCodeLines (L3[0], &L3[1], 1)) {
2176 if (LineFullMatch (L3[1], "\tjsr\tldaui")) {
2177 /* Load char indirect */
2178 L = NewLineAfter (L, "\tldx\t#$00");
2179 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2182 } else if (LineFullMatch (L3[1], "\tsta\tptr1") &&
2183 GetNextCodeLines (L3[1], &L3[2], 3) &&
2184 LineFullMatch (L3[2], "\tstx\tptr1+1") &&
2185 LineFullMatch (L3[3], "\tldx\t#$00") &&
2186 LineFullMatch (L3[4], "\tlda\t(ptr1,x)")) {
2188 /* Load char indirect, inlined */
2189 L = NewLineAfter (L, "\tldx\t#$00");
2190 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2194 } else if (LineFullMatch (L3[1], "\tjsr\tldaxi")) {
2195 /* Load word indirect */
2196 L = NewLineAfter (L, "\tldy\t#$01");
2197 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2198 L = NewLineAfter (L, "\ttax");
2199 L = NewLineAfter (L, "\tdey");
2200 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2204 } else if (LineFullMatch (L3[1], "\tjsr\tpushax")) {
2205 if (GetNextCodeLines (L3[1], &L3[2], 2) &&
2206 LineMatch (L3[2], "\tlda\t") &&
2207 LineFullMatch (L3[3], "\tjsr\tstaspp")) {
2209 /* Store to pointer */
2210 L = NewLineAfter (L, L3[2]->Line);
2211 L = NewLineAfter (L, "\tldy\t#$00");
2212 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2216 } else if (GetNextCodeLines (L3[1], &L3[2], 3) &&
2217 LineMatch (L3[2], "\tldy\t#$") &&
2218 LineFullMatch (L3[3], "\tlda\t(sp),y") &&
2219 LineFullMatch (L3[4], "\tjsr\tstaspp")) {
2221 /* Beware: We have to correct the stack offset, since we will
2222 * remove the pushax instruction!
2224 sprintf (L3[2]->Line+7, "%02X", GetHexNum (L3[2]->Line+7)-2);
2226 /* Store to pointer */
2227 L = NewLineAfter (L, L3[2]->Line);
2228 L = NewLineAfter (L, L3[3]->Line);
2229 L = NewLineAfter (L, "\tldy\t#$00");
2230 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2239 /* If we need to load a/x, add the code */
2241 L = NewLineAfter (L, "\ttax");
2242 L = NewLineAfter (L, "\tlda\tptr1");
2245 /* Remove the code that is no longer needed */
2246 FreeLines (L2[0], L2[LinesToRemove-1]);
2248 /* Return the new line and success */
2249 *Start = NextCodeLine (L);
2255 static void OptPtrOps (void)
2256 /* Optimize several pointer and array constructs */
2260 Line* L = FirstCode;
2263 if (OptPtrOps1 (&L)) {
2265 } else if (OptPtrOps2 (&L)) {
2269 /* Search for the following sequence:
2277 * and replace it by:
2284 else if (LineFullMatch (L, "\tlda\tregsave") && /* Match on start */
2285 GetNextCodeLines (L, L2, 4) && /* Fetch next lines */
2286 LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */
2287 LineFullMatch (L2 [1], "\tjsr\tpushax") &&
2288 LineMatch (L2 [2], "\tlda\t#$") &&
2289 LineFullMatch (L2 [3], "\tjsr\tstaspp")) {
2291 /* Found the sequence, replace it */
2292 L = ReplaceLine (L, L2 [2]->Line); /* lda #$.. */
2293 L2 [0] = ReplaceLine (L2 [0], "\tldy\t#$00");
2294 L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
2296 /* Free the remaining lines */
2297 FreeLines (L2 [2], L2 [3]);
2300 /* Search for the following sequence:
2306 * and replace it by:
2312 else if (LineFullMatch (L, "\tlda\tregsave") && /* Match on start */
2313 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
2314 LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */
2315 LineFullMatch (L2 [1], "\tjsr\tldaui")) {
2317 /* Found the sequence, replace it */
2318 L = ReplaceLine (L, "\tldx\t#$00");
2319 L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
2321 /* Free the remaining lines */
2326 * Search for the following sequence:
2335 * and replace it by:
2346 else if (LineFullMatch (L, "\tlda\tregsave") &&
2347 GetNextCodeLines (L, L2, 5) &&
2348 LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
2349 LineFullMatch (L2 [1], "\tjsr\tpushax") &&
2350 LineMatch (L2 [2], "\tldx\t#$") &&
2351 LineMatch (L2 [3], "\tlda\t#$") &&
2352 LineFullMatch (L2 [4], "\tjsr\tstaxspp")) {
2354 /* Found the sequence, replace it */
2355 L = ReplaceLine (L, "\tldy\t#$01");
2356 L2 [0] = ReplaceLine (L2 [0], L2 [2]->Line);
2357 L2 [0]->Line [3] = 'a';
2358 L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
2359 L2 [4] = ReplaceLine (L2 [4], L2 [3]->Line);
2360 L2 [2] = ReplaceLine (L2 [2], "\ttax");
2361 L2 [3] = ReplaceLine (L2 [3], "\tdey");
2362 L = NewLineAfter (L2 [4], "\tsta\t(regsave),y");
2366 * Search for the following sequence:
2375 * and replace it by:
2381 else if (LineFullMatch (L, "\tlda\tregsave") &&
2382 GetNextCodeLines (L, L2, 5) &&
2383 LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
2384 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2385 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2386 LineFullMatch (L2 [3], "\tldx\t#$00") &&
2387 LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
2389 /* Found the sequence, replace it */
2390 L = ReplaceLine (L, "\tldx\t#$00");
2391 L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
2393 /* Remove the remaining lines */
2394 FreeLines (L2 [1], L2 [4]);
2397 /* Search for the following sequence:
2403 * and replace it by:
2412 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2413 GetNextCodeLines (L, L2, 2) &&
2414 LineMatch (L2 [0], "\tlda\t") &&
2415 LineFullMatch (L2 [1], "\tjsr\tstaspp")) {
2417 /* Found the sequence, replace it */
2418 L = ReplaceLine (L, "\tsta\tptr1");
2419 L2 [1] = ReplaceLine (L2 [1], L2 [0]->Line); /* lda ... */
2420 L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
2421 L2 [2] = NewLineAfter (L2 [1], "\tldy\t#$00");
2422 L = NewLineAfter (L2 [2], "\tsta\t(ptr1),y");
2425 /* Search for the following sequence:
2432 * and replace it by:
2441 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2442 GetNextCodeLines (L, L2, 3) &&
2443 LineMatch (L2 [0], "\tlda\t") &&
2444 LineMatch (L2 [1], "\tldy\t#$") &&
2445 LineFullMatch (L2 [2], "\tjsr\tstaspidx")) {
2447 /* Found the sequence, replace it */
2448 L = ReplaceLine (L, "\tsta\tptr1");
2449 L = NewLineAfter (L, "\tstx\tptr1+1");
2450 L2 [2] = ReplaceLine (L2 [2], "\tsta\t(ptr1),y");
2453 /* Search for the following sequence:
2460 * and replace it by:
2469 * Beware: Since we remove a call to a function that changes the stack
2470 * pointer, we have to adjust the stack address for the lda.
2473 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2474 GetNextCodeLines (L, L2, 3) &&
2475 LineMatch (L2 [0], "\tldy\t#$") &&
2476 LineFullMatch (L2 [1], "\tlda\t(sp),y") &&
2477 LineFullMatch (L2 [2], "\tjsr\tstaspp")) {
2479 /* Found the sequence, replace it. First create a new load
2480 * instruction for the changed stack offset.
2483 sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
2484 L = ReplaceLine (L, "\tsta\tptr1");
2485 L2 [1] = ReplaceLine (L2 [1], Buf); /* ldy ... */
2486 L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
2487 L2 [2] = ReplaceLine (L2 [2], "\tlda\t(sp),y");
2488 L2 [3] = NewLineAfter (L2 [2], "\tldy\t#$00");
2489 L = NewLineAfter (L2 [3], "\tsta\t(ptr1),y");
2492 /* Search for the following sequence:
2500 * and replace it by:
2509 * Beware: Since we remove a call to a function that changes the stack
2510 * pointer, we have to adjust the stack address for the lda.
2513 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2514 GetNextCodeLines (L, L2, 4) &&
2515 LineMatch (L2 [0], "\tldy\t#$") &&
2516 LineFullMatch (L2 [1], "\tlda\t(sp),y") &&
2517 LineMatch (L2 [2], "\tldy\t#$") &&
2518 LineFullMatch (L2 [3], "\tjsr\tstaspidx")) {
2520 /* Found the sequence, replace it. First create a new load
2521 * instruction for the changed stack offset.
2524 sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
2525 L = ReplaceLine (L, "\tsta\tptr1");
2526 L = NewLineAfter (L, "\tstx\tptr1+1");
2527 L2 [0] = ReplaceLine (L2 [0], Buf); /* ldy ... */
2528 L2 [3] = ReplaceLine (L2 [3], "\tsta\t(ptr1),y");
2531 /* Search for the following sequence:
2544 * and replace it by:
2552 * The load of X may be omitted if X is not used below.
2554 else if (LineMatch (L, "\tldax\t_") &&
2555 GetNextCodeLines (L, L2, 9) &&
2556 LineMatch (L2 [0], "\tldy\t#$") &&
2557 LineFullMatch (L2 [1], "\tclc") &&
2558 LineFullMatch (L2 [2], "\tadc\t(sp),y") &&
2559 LineFullMatch (L2 [3], "\tbcc\t*+3") &&
2560 LineFullMatch (L2 [4], "\tinx") &&
2561 LineFullMatch (L2 [5], "\tsta\tptr1") &&
2562 LineFullMatch (L2 [6], "\tstx\tptr1+1") &&
2563 LineFullMatch (L2 [7], "\tldx\t#$00") &&
2564 LineFullMatch (L2 [8], "\tlda\t(ptr1,x)")) {
2566 /* Found the sequence, replace it */
2568 strcpy (Label, L->Line + 6); /* Remember the label */
2569 L = ReplaceLine (L, L2 [0]->Line); /* ldy .. */
2570 L = NewLineAfter (L, "\tlda\t(sp),y");
2571 L = NewLineAfter (L, "\ttay");
2572 if (RegXUsed (L2[8])) {
2573 L = NewLineAfter (L, "\tldx\t#$00");
2575 L = NewLineAfter (L, "\tlda\t%s,y", Label);
2577 /* Remove the remaining stuff. There may be hints between the
2578 * instructions, remove them too
2580 FreeLines (L2[0], L2 [8]);
2604 * This change will cost 2 cycles, but it saves a lot of code (6 bytes
2605 * per occurrence), so we will accept the overhead. It may even be
2606 * possible to rewrite the library routine to get rid of the additional
2609 else if (LineMatch (L, "\tldy\t#$") &&
2610 GetNextCodeLines (L, L2, 6) &&
2611 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
2612 LineFullMatch (L2 [1], "\ttax") &&
2613 LineFullMatch (L2 [2], "\tdey") &&
2614 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
2615 LineMatch (L2 [4], "\tldy\t#$") &&
2616 LineFullMatch (L2 [5], "\tjsr\tldauidx")) {
2618 /* Found - replace it */
2619 L2 [4]->Line [3] = 'x'; /* Change to ldx */
2620 if (LineFullMatch (L, "\tldy\t#$01")) {
2621 /* Word at offset zero */
2623 L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp");
2625 ReplaceLine (L2 [5], "\tjsr\tldauiysp");
2628 /* Delete the remaining lines */
2629 FreeLines (L2 [0], L2 [3]);
2653 * This change will has an overhead of 10 cycles, but it saves 11(!)
2654 * bytes per invocation. Maybe we should apply only if FavourSize is
2657 else if (LineMatch (L, "\tldy\t#$") &&
2658 GetNextCodeLines (L, L2, 8) &&
2659 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
2660 LineFullMatch (L2 [1], "\ttax") &&
2661 LineFullMatch (L2 [2], "\tdey") &&
2662 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
2663 LineFullMatch (L2 [4], "\tsta\tptr1") &&
2664 LineFullMatch (L2 [5], "\tstx\tptr1+1") &&
2665 LineFullMatch (L2 [6], "\tldx\t#$00") &&
2666 LineFullMatch (L2 [7], "\tlda\t(ptr1,x)")) {
2668 /* Found - replace it */
2669 if (LineFullMatch (L, "\tldy\t#$01")) {
2670 /* Word at offset zero */
2672 L = ReplaceLine (L2 [0], "\tjsr\tldau00sp");
2674 ReplaceLine (L2 [0], "\tjsr\tldau0ysp");
2677 /* Delete the remaining lines */
2678 FreeLines (L2 [1], L2 [7]);
2682 L = NextCodeLine (L);
2688 static void OptRegVars (void)
2689 /* Optimize register variable uses */
2693 Line* L = FirstCode;
2696 /* Search for the following sequence:
2702 * and replace it by:
2708 if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2709 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
2710 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2711 LineFullMatch (L2 [1], "\tjsr\tldaui") &&
2712 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2713 strcmp (L2 [0]->Line + 14, "+1") == 0) {
2716 sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
2718 /* Found the sequence, replace it */
2719 L = ReplaceLine (L, "\tldx\t#$00");
2720 L2 [0] = ReplaceLine (L2 [0], Buf);
2722 /* Free the remaining lines */
2726 /* Search for the following sequence:
2735 * and replace it by:
2741 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2742 GetNextCodeLines (L, L2, 5) && /* Fetch next lines */
2743 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2744 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2745 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2746 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2747 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2748 LineFullMatch (L2 [3], "\tldx\t#$00") &&
2749 LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
2752 sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
2754 /* Found the sequence, replace it */
2755 L = ReplaceLine (L, "\tldx\t#$00");
2756 L2 [0] = ReplaceLine (L2 [0], Buf);
2758 /* Free the remaining lines */
2759 FreeLines (L2 [1], L2 [4]);
2762 /* Search for the following sequence:
2769 * and replace it by:
2776 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2777 GetNextCodeLines (L, L2, 3) && /* Fetch next lines */
2778 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2779 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2780 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2781 LineMatch (L2 [1], "\tldy\t#$") &&
2782 LineFullMatch (L2 [2], "\tjsr\tldauidx")) {
2785 sprintf (Buf, "\tlda\t(%s),y", L->Line + 5);
2787 /* Found the sequence, replace it */
2788 L = ReplaceLine (L, L2 [1]->Line);
2789 L2 [0] = ReplaceLine (L2 [0], "\tldx\t#$00");
2790 L2 [1] = ReplaceLine (L2 [1], Buf);
2792 /* Free the remaining lines */
2796 /* Search for the following sequence:
2806 * and replace it by:
2812 * The source form is not generated by the parser but by the optimizer.
2814 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2815 GetNextCodeLines (L, L2, 6) && /* Fetch next lines */
2816 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2817 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2818 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2819 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2820 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2821 LineMatch (L2 [3], "\tlda\t") &&
2822 LineMatch (L2 [4], "\tldy\t#$") &&
2823 LineMatch (L2 [5], "\tsta\t(ptr1),y")) {
2826 sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
2828 /* Found the sequence, replace it */
2829 L2 [5] = ReplaceLine (L2 [5], Buf);
2831 /* Free the remaining lines */
2832 FreeLines (L, L2 [2]);
2834 /* Make the line pointer valid again */
2838 /* Search for the following sequence:
2849 * and replace it by:
2856 * The source form is not generated by the parser but by the optimizer.
2858 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2859 GetNextCodeLines (L, L2, 7) && /* Fetch next lines */
2860 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2861 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2862 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2863 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2864 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2865 LineMatch (L2 [3], "\tldy\t#$") &&
2866 LineFullMatch (L2 [4], "\tlda\t(sp),y") &&
2867 LineMatch (L2 [5], "\tldy\t#$") &&
2868 LineMatch (L2 [6], "\tsta\t(ptr1),y")) {
2871 sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
2873 /* Found the sequence, replace it */
2874 L2 [6] = ReplaceLine (L2 [6], Buf);
2876 /* Free the remaining lines */
2877 FreeLines (L, L2 [2]);
2879 /* Make the line pointer valid again */
2884 L = NextCodeLine (L);
2890 static void OptDoubleJumps (void)
2891 /* Remove/rearrange jumps that jump to other jumps */
2893 static const char* Jumps [] = {
2904 Line* L = FirstCode;
2909 /* Is this a jump? */
2910 while ((I = LineMatchX (L, Jumps)) >= 0) {
2912 /* Yes. Get the target label */
2913 Line* Target = GetTargetLine (L->Line + 5);
2915 /* Target points to the label itself. Skip lines until we reach
2916 * one that is not a label.
2918 Target = NextInstruction (Target);
2920 /* Be sure, this line is not the same as the one the jump is
2921 * in (this happens if there is an empty loop).
2927 if (LineMatch (Target, "\tjmp\t")) {
2929 /* The target is itself a jump. If this is a short branch, get
2930 * the final target and check if it is in reach. Bail out if
2933 if (L->Line[1] == 'b') {
2934 Line* FinalTarget = GetTargetLine (Target->Line+5);
2935 FinalTarget = NextInstruction (FinalTarget);
2936 if ((D = GetJumpDistance (L, FinalTarget)) >= 123) {
2941 /* Make sure the jump does indeed point to another label.
2942 * It may happen that this is not the case for some endless
2943 * loop (while(1) and similar).
2945 if (strcmp (L->Line+5, Target->Line+5) == 0) {
2946 /* Same label, bail out */
2950 /* Use the label in the original jump instead */
2951 L = ReplaceLine (L, "%.5s%s", L->Line, Target->Line+5);
2953 } else if (I < 2 && LineMatch (Target, Jumps [I])) {
2955 /* Conditional jump. Use final label */
2956 strcpy (L->Line+5, Target->Line+5);
2964 L = NextCodeLine (L);
2970 static void OptJumpRTS (void)
2971 /* Replace jumps to an RTS by an RTS */
2973 Line* L = FirstCode;
2975 /* Is this a jump to a numbered label? */
2976 if (LineMatch (L, "\tjmp\t") && L->Line [5] == 'L' && isdigit (L->Line [6])) {
2978 /* Yes. Get the target label */
2979 Line* Target = GetTargetLine (L->Line+5);
2981 /* Target points to the label itself. Get the next line */
2982 Target = NextCodeLine (Target);
2983 if (LineFullMatch (Target, "\trts")) {
2984 /* Replace the jump by an RTS */
2985 L = ReplaceLine (L, "\trts");
2988 L = NextCodeLine (L);
2994 static void OptBoolTransforms (void)
2995 /* Try to remove the boolean transformation subroutines where they aren't
3001 const char* BranchTarget;
3003 Line* L = FirstCode;
3006 /* Search for a boolean transformer followed by a conditional jump. */
3007 if (LineMatch (L, "\tjsr\tbool") &&
3008 GetNextCodeLines (L, L2, 1) &&
3009 IsCondJump (L2 [0])) {
3011 /* Make the boolean transformer unnecessary by changing the
3012 * the conditional jump to evaluate the condition flags that
3013 * are set after the compare directly. Note: jeq jumps if
3014 * the condition is not met, jne jumps if the condition is met.
3017 /* Get the condition code */
3018 int Cond = FindCond (L->Line + 9);
3024 /* Invert the code if we jump on condition not met. */
3025 if (L2[0]->Line [2] == 'e' && L2[0]->Line [3] == 'q') {
3026 /* Jumps if condition false, invert condition */
3027 Cond = CmpInvertTab [Cond];
3030 /* For easier reading, get a pointer to the jump target */
3031 BranchTarget = L2[0]->Line+5;
3033 /* Check if we can replace the jump (sometimes we would need two
3034 * conditional jumps, we will not handle that for now since it
3035 * has some complications - both jumps may be far jumps for
3036 * example making the jumps more costly than the bool transformer
3037 * subroutine). If we cannot replace the jump, bail out.
3042 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3046 L = ReplaceLine (L, "\tjne\t%s", BranchTarget);
3050 Label = AllocLabel ();
3051 L = ReplaceLine (L, "\tbeq\tL%04X", Label);
3052 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3053 L = NewLabelAfter(L, Label);
3057 L = ReplaceLine (L, "\tjpl\t%s", BranchTarget);
3061 L = ReplaceLine (L, "\tjmi\t%s", BranchTarget);
3065 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3066 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3070 Label = AllocLabel ();
3071 L = ReplaceLine (L, "\tbeq\tL%04X", Label);
3072 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3073 L = NewLabelAfter(L, Label);
3077 L = ReplaceLine (L, "\tjcs\t%s", BranchTarget);
3081 L = ReplaceLine (L, "\tjcc\t%s", BranchTarget);
3085 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3086 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3090 Internal ("Unknown jump condition: %u", Cond);
3094 /* Remove the old stuff */
3100 L = NextCodeLine (L);
3106 static void OptCompares2 (void)
3107 /* Try to optimize the integer compare subroutines. */
3111 const char* BranchTarget;
3114 Line* L = FirstCode;
3132 if (LineMatch (L, "\tlda\t") &&
3133 GetNextCodeLines (L, L2, 5) &&
3134 IsLoadAX (L, L2[0]) &&
3135 LineFullMatch (L2[1], "\tcpx\t#$00") &&
3136 LineFullMatch (L2[2], "\tbne\t*+4") &&
3137 LineFullMatch (L2[3], "\tcmp\t#$00") &&
3138 IsCondJump (L2[4])) {
3140 /* Replace the load of X by an ora */
3141 L2[0]->Line[1] = 'o';
3142 L2[0]->Line[2] = 'r';
3143 L2[0]->Line[3] = 'a';
3145 /* Remove unneeded stuff */
3146 FreeLines (L2[1], L2[3]);
3150 /* Same for local variables: Replace
3170 else if (LineMatch (L, "\tldy\t#$") &&
3171 GetNextCodeLines (L, L2, 8) &&
3172 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
3173 LineFullMatch (L2[1], "\ttax") &&
3174 LineFullMatch (L2[2], "\tdey") &&
3175 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
3176 LineFullMatch (L2[4], "\tcpx\t#$00") &&
3177 LineFullMatch (L2[5], "\tbne\t*+4") &&
3178 LineFullMatch (L2[6], "\tcmp\t#$00") &&
3179 IsCondJump (L2[7])) {
3181 /* Replace the second load by an ora */
3182 L2[3]->Line[1] = 'o';
3183 L2[3]->Line[2] = 'r';
3184 L2[3]->Line[3] = 'a';
3186 /* Remove unneeded stuff */
3188 FreeLines (L2[4], L2[6]);
3192 /* Search for the call to a compare subroutine followed by a
3195 else if (LineMatch (L, "\tjsr\ttos") &&
3196 (L2[0] = NextCodeLine (L)) != 0 &&
3197 IsCondJump (L2[0])) {
3199 /* Extract the condition from the function name and branch */
3200 C = CheckAndGetIntCmp (L, L2[0]);
3202 /* Something is wrong */
3206 /* Replace the subroutine call by a cheaper one */
3207 L = ReplaceLine (L, "\tjsr\ttosicmp");
3209 /* For easier reading, get a pointer to the jump target */
3210 BranchTarget = L2[0]->Line+5;
3212 /* Check if we can replace the jump (sometimes we would need two
3213 * conditional jumps, we will not handle that for now since it
3214 * has some complications - both jumps may be far jumps for
3215 * example making the jumps more costly than the bool transformer
3216 * subroutine). If we cannot replace the jump, bail out.
3221 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3225 L = NewLineAfter (L, "\tjne\t%s", BranchTarget);
3229 Label = AllocLabel ();
3230 L = NewLineAfter (L, "\tbeq\tL%04X", Label);
3231 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3232 L = NewLabelAfter(L, Label);
3236 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3240 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3244 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3245 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3249 Label = AllocLabel ();
3250 L = NewLineAfter (L, "\tbeq\tL%04X", Label);
3251 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3252 L = NewLabelAfter(L, Label);
3256 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3260 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3264 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3265 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3269 Internal ("Unknown jump condition: %u", C);
3273 /* Remove the old stuff */
3278 L = NextCodeLine (L);
3284 static void OptTests (void)
3285 /* Remove unnecessary tests */
3289 const char* BitOps [] = {
3296 /* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
3303 Line* L = FirstCode;
3306 /* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
3313 if ((LineMatch (L, "\tlda\t") ||
3314 LineMatch (L, "\tand\t") ||
3315 LineMatch (L, "\tora\t") ||
3316 LineMatch (L, "\teor\t")) &&
3317 GetNextCodeLines (L, L2, 2) &&
3318 (LineFullMatch (L2 [0], "\ttay") ||
3319 LineFullMatch (L2 [0], "\tcmp\t#$00")) &&
3320 IsCondJump (L2 [1])) {
3322 /* We can remove the tay */
3333 * and remove the tax.
3335 else if (LineMatchX (L, BitOps) >= 0 &&
3336 GetNextCodeLines (L, L2, 2) &&
3337 LineFullMatch (L2[0], "\ttax") &&
3338 IsCondJump (L2[1])) {
3340 /* Remove the tax including a hint line of there is one */
3341 if (LineFullMatch (L2[0]->Prev, "+forcetest")) {
3342 FreeLine (L2[0]->Prev);
3346 /* If the line before L loads X, this is useless and may be removed */
3347 L2[0] = PrevCodeLine (L);
3348 if (LineFullMatch (L2[0], "\tldx\t#$00")) {
3354 /* Search for the sequence
3365 else if (LineMatch (L, "\tstx\t") &&
3366 GetNextCodeLines (L, L2, 2) &&
3367 LineFullMatch (L2[0], "\tstx\ttmp1") &&
3368 LineFullMatch (L2[1], "\tora\ttmp1")) {
3370 /* Found, replace it */
3371 L = NewLineAfter (L, "\tora\t%s", L->Line+5);
3373 /* Remove remaining stuff */
3374 FreeLines (L2[0], L2[1]);
3380 L = NextCodeLine (L);
3386 static void OptNeg (void)
3387 /* Optimize the "bnegax/jeq" and "bnegax/jne" sequences */
3391 Line* L = FirstCode;
3394 /* Search for the sequence:
3400 * and replace it by:
3405 if (LineMatch (L, "\tlda\t") && /* Match on start */
3406 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
3407 LineFullMatch (L2 [0], "\tjsr\tbnega") &&
3408 IsCondJump (L2 [1])) {
3410 /* Found the sequence, replace it */
3412 InvertZJump (L2 [1]);
3416 /* Search for the sequence:
3434 else if (LineMatch (L, "\tldy\t#$") &&
3435 GetNextCodeLines (L, L2, 6) &&
3436 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
3437 LineFullMatch (L2[1], "\ttax") &&
3438 LineFullMatch (L2[2], "\tdey") &&
3439 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
3440 LineFullMatch (L2[4], "\tjsr\tbnegax") &&
3441 IsCondJump (L2[5])) {
3443 L2[1] = ReplaceLine (L2[1], "\tdey");
3444 L2[2] = ReplaceLine (L2[2], "\tora\t(sp),y");
3445 FreeLines (L2[3], L2[4]);
3446 InvertZJump (L2[5]);
3450 /* Search for the sequence:
3463 else if (LineMatch (L, "\tlda\t") &&
3464 GetNextCodeLines (L, L2, 3) &&
3465 IsLoadAX (L, L2[0]) &&
3466 LineFullMatch (L2[1], "\tjsr\tbnegax") &&
3467 IsCondJump (L2[2])) {
3469 /* Replace the load of X by ora */
3470 L2[0]->Line[1] = 'o';
3471 L2[0]->Line[2] = 'r';
3472 L2[0]->Line[3] = 'a';
3474 InvertZJump (L2[2]);
3478 /* Search for the sequence:
3484 * and replace it by:
3490 else if (LineMatch (L, "\tjsr\t_") && /* Match on start */
3491 GetNextCodeLines (L, L2, 2) &&
3492 LineMatch (L2 [0], "\tjsr\tbnega") &&
3493 IsCondJump (L2 [1])) {
3495 if (LineFullMatch (L2 [0], "\tjsr\tbnega")) {
3497 L2 [0] = ReplaceLine (L2 [0], "\ttax"); /* Test a */
3500 L2 [0] = ReplaceLine (L2 [0], "\tstx\ttmp1");
3501 NewLineAfter (L2 [0], "\tora\ttmp1");
3504 /* Invert the jump */
3505 InvertZJump (L2 [1]);
3510 L = NextCodeLine (L);
3516 static void OptTriples (void)
3517 /* Replace code triples */
3519 static const char* Pat1 [] = {
3527 static const char* Pat2 [] = {
3535 static const char* Replace [] = {
3543 Line* L = FirstCode;
3545 int I = LineFullMatchX (L, Pat1);
3547 /* We found the first match, get the next line */
3548 Line* L2 = NextCodeLine (L);
3549 if (L2 && LineFullMatch (L2, Pat2 [I])) {
3550 /* Found. Replace by the short call */
3552 L = ReplaceLine (L, Replace [I]);
3556 L = NextCodeLine (L);
3562 static Line* OptOneBlock (Line* L)
3563 /* Optimize the register contents inside one basic block */
3565 static const char* Compares [] = {
3566 "\tjsr\ttoseq00", "\tjsr\ttoseqa0", "\tjsr\ttoseqax",
3567 "\tjsr\ttoseqeax", "\tjsr\ttosne00", "\tjsr\ttosnea0",
3568 "\tjsr\ttosneax", "\tjsr\ttosneeax", "\tjsr\ttoslt00",
3569 "\tjsr\ttoslta0", "\tjsr\ttosltax", "\tjsr\ttosult00",
3570 "\tjsr\ttosulta0", "\tjsr\ttosultax", "\tjsr\ttoslteax",
3571 "\tjsr\ttosulteax", "\tjsr\ttosle00", "\tjsr\ttoslea0",
3572 "\tjsr\ttosleax", "\tjsr\ttosule00", "\tjsr\ttosulea0",
3573 "\tjsr\ttosuleax", "\tjsr\ttosleeax", "\tjsr\ttosuleeax",
3574 "\tjsr\ttosgt00", "\tjsr\ttosgta0", "\tjsr\ttosgtax",
3575 "\tjsr\ttosugt00", "\tjsr\ttosugta0", "\tjsr\ttosugtax",
3576 "\tjsr\ttosgteax", "\tjsr\ttosugteax", "\tjsr\ttosge00",
3577 "\tjsr\ttosgea0", "\tjsr\ttosgeax", "\tjsr\ttosuge00",
3578 "\tjsr\ttosugea0", "\tjsr\ttosugeax", "\tjsr\ttosgeeax",
3583 static const char* MakeBool [] = {
3584 "\tjsr\tbooleq", "\tjsr\tboolne", "\tjsr\tboollt",
3585 "\tjsr\tboolle", "\tjsr\tboolgt", "\tjsr\tboolge",
3586 "\tjsr\tboolult", "\tjsr\tboolule", "\tjsr\tboolugt",
3591 int A = -1; /* Contents of A register */
3592 int X = -1; /* Contents of X register */
3593 int Y = -1; /* Contents of Y register */
3598 while (L && !IsLabel (L)) {
3600 /* Handle all instructions. All instructions not tested here have
3601 * no effects on the register contents.
3604 if (L->Line [0] == '+') {
3605 /* This is a hint */
3606 if (LineMatch (L, "+a:")) {
3607 /* Information about a */
3608 switch (L->Line [3]) {
3609 case '!': A = -1; break;
3610 case '=': A = GetHexNum (L->Line + 4); break;
3612 } else if (LineMatch (L, "+x:")) {
3613 /* The code generator tells something about the x register */
3614 switch (L->Line [3]) {
3615 case '!': X = -1; break;
3616 case '=': X = GetHexNum (L->Line + 4); break;
3618 } else if (LineMatch (L, "+y:")) {
3619 /* Information about the y register */
3620 switch (L->Line [3]) {
3621 case '!': Y = -1; break;
3622 case '=': Y = GetHexNum (L->Line + 4); break;
3625 } else if (LineMatch (L, "\tadc\t")) {
3627 } else if (LineMatch (L, "\tand\t")) {
3629 } else if (LineFullMatch (L, "\tasl\ta")) {
3631 A = (A << 1) & 0xFF;
3633 } else if (LineFullMatch (L, "\tdex")) {
3635 } else if (LineFullMatch (L, "\tdey")) {
3637 } else if (LineMatch (L, "\teor")) {
3639 } else if (LineFullMatch (L, "\tinx")) {
3641 } else if (LineFullMatch (L, "\tiny")) {
3643 } else if (LineFullMatch (L, "\tjsr\taddeq0sp")) {
3644 /* We know about this function */
3647 } else if (LineFullMatch (L, "\tjsr\taddeqysp")) {
3648 /* We know about this function */
3651 } else if (LineFullMatch (L, "\tjsr\taxulong")) {
3652 /* We know about this function and we're trying to replace it by
3653 * inline code if we have already a register that contains zero.
3658 } else if (X == 0) {
3660 } else if (Y == 0) {
3666 /* We cannot replace the code, but we know about the results */
3669 L = ReplaceLine (L, "\tst%c\tsreg", C);
3670 NewLineAfter (L, "\tst%c\tsreg+1", C);
3672 } else if (LineFullMatch (L, "\tjsr\tbnega")) {
3673 /* We know about this function */
3676 } else if (LineFullMatch (L, "\tjsr\tbnegax")) {
3677 /* We know about this function */
3680 } else if (LineFullMatch (L, "\tjsr\tbnegeax")) {
3681 /* We know about this function */
3684 } else if (LineFullMatch (L, "\tjsr\tcomplax")) {
3685 /* We know about this function */
3692 } else if (LineFullMatch (L, "\tjsr\tdecax1")) {
3693 /* We know about this function */
3695 } else if (LineFullMatch (L, "\tjsr\tdecax2")) {
3696 /* We know about this function */
3698 } else if (LineFullMatch (L, "\tjsr\tdecaxy")) {
3699 /* We know about this function */
3701 } else if (LineFullMatch (L, "\tjsr\tdeceaxy")) {
3702 /* We know about this function */
3704 } else if (LineFullMatch (L, "\tjsr\tincax1")) {
3705 /* We know about this function */
3707 } else if (LineFullMatch (L, "\tjsr\tincax2")) {
3708 /* We know about this function */
3710 } else if (LineFullMatch (L, "\tjsr\tinceaxy")) {
3711 /* We know about this function */
3713 } else if (LineFullMatch (L, "\tjsr\tladdeq")) {
3714 /* We know about this function */
3717 } else if (LineFullMatch (L, "\tjsr\tladdeqb")) {
3718 /* We know about this function */
3721 } else if (LineFullMatch (L, "\tjsr\tlbneg")) {
3722 /* We know about this function */
3725 } else if (LineFullMatch (L, "\tjsr\tldai")) {
3726 /* We know about this function */
3729 } else if (LineFullMatch (L, "\tjsr\tldaidx")) {
3730 /* We know about this function */
3732 } else if (LineFullMatch (L, "\tjsr\tldau00sp")) {
3733 /* We know about this function */
3737 } else if (LineFullMatch (L, "\tjsr\tldau0ysp")) {
3738 /* We know about this function */
3742 } else if (LineFullMatch (L, "\tjsr\tldaui")) {
3743 /* We know about this function */
3747 } else if (LineFullMatch (L, "\tjsr\tldaui0sp")) {
3751 } else if (LineFullMatch (L, "\tjsr\tldauidx")) {
3752 /* We know about this function */
3755 } else if (LineFullMatch (L, "\tjsr\tldauiysp")) {
3756 /* We know about this function */
3760 } else if (LineFullMatch (L, "\tjsr\tldax0sp")) {
3761 /* We know about this function */
3764 } else if (LineFullMatch (L, "\tjsr\tldaxi")) {
3765 /* We know about this function */
3768 } else if (LineFullMatch (L, "\tjsr\tldaxidx")) {
3769 /* We know about this function */
3772 } else if (LineFullMatch (L, "\tjsr\tldaxysp")) {
3773 /* We know about this function */
3776 } else if (LineFullMatch (L, "\tjsr\tldeaxi")) {
3777 /* We know about this function */
3780 } else if (LineFullMatch (L, "\tjsr\tldeaxidx")) {
3781 /* We know about this function */
3784 } else if (LineFullMatch (L, "\tjsr\tlsubeq")) {
3785 /* We know about this function */
3788 } else if (LineFullMatch (L, "\tjsr\tlsubeqb")) {
3789 /* We know about this function */
3792 } else if (LineFullMatch (L, "\tjsr\tnegax")) {
3793 /* We know about this function */
3795 } else if (LineFullMatch (L, "\tjsr\tnegeax")) {
3796 /* We know about this function */
3798 } else if (LineFullMatch (L, "\tjsr\tpush0")) {
3799 /* We know about this function */
3803 } else if (LineFullMatch (L, "\tjsr\tpush1")) {
3804 /* We know about this function */
3808 } else if (LineFullMatch (L, "\tjsr\tpush2")) {
3809 /* We know about this function */
3813 } else if (LineFullMatch (L, "\tjsr\tpush3")) {
3814 /* We know about this function */
3818 } else if (LineFullMatch (L, "\tjsr\tpush4")) {
3819 /* We know about this function */
3823 } else if (LineFullMatch (L, "\tjsr\tpush5")) {
3824 /* We know about this function */
3828 } else if (LineFullMatch (L, "\tjsr\tpush6")) {
3829 /* We know about this function */
3833 } else if (LineFullMatch (L, "\tjsr\tpush7")) {
3834 /* We know about this function */
3838 } else if (LineFullMatch (L, "\tjsr\tpusha")) {
3839 /* We know about this function */
3841 } else if (LineFullMatch (L, "\tjsr\tpusha0")) {
3842 /* We know about this function
3843 * If X is already zero, we may call pushax instead and save two
3847 L = ReplaceLine (L, "\tjsr\tpushax");
3851 } else if (LineFullMatch (L, "\tjsr\tpushax")) {
3852 /* We know about this function */
3854 } else if (LineFullMatch (L, "\tjsr\tpushc0")) {
3855 /* We know about this function */
3858 } else if (LineFullMatch (L, "\tjsr\tpushc1")) {
3859 /* We know about this function */
3862 } else if (LineFullMatch (L, "\tjsr\tpushc2")) {
3863 /* We know about this function */
3866 } else if (LineFullMatch (L, "\tjsr\tpushw")) {
3867 /* We know about this function (calls pushax) */
3870 } else if (LineFullMatch (L, "\tjsr\tpushw0sp")) {
3871 /* We know about this function(calls pushax) */
3874 } else if (LineFullMatch (L, "\tjsr\tpushwidx")) {
3875 /* We know about this function (calls pushax) */
3878 } else if (LineFullMatch (L, "\tjsr\tpushwysp")) {
3879 /* We know about this function (calls pushax) */
3882 } else if (LineFullMatch (L, "\tjsr\tresteax")) {
3883 /* We know about this function */
3885 } else if (LineFullMatch (L, "\tjsr\tsaveeax")) {
3886 /* We know about this function */
3887 /* Changes nothing */
3888 } else if (LineFullMatch (L, "\tjsr\tshrax1")) {
3889 /* We know about this function */
3891 } else if (LineFullMatch (L, "\tjsr\tshrax2")) {
3892 /* We know about this function */
3894 } else if (LineFullMatch (L, "\tjsr\tshrax3")) {
3895 /* We know about this function */
3897 } else if (LineFullMatch (L, "\tjsr\tshreax1")) {
3898 /* We know about this function */
3900 } else if (LineFullMatch (L, "\tjsr\tshreax2")) {
3901 /* We know about this function */
3903 } else if (LineFullMatch (L, "\tjsr\tshreax3")) {
3904 /* We know about this function */
3906 } else if (LineFullMatch (L, "\tjsr\tstaspp")) {
3907 /* We know about this function */
3909 } else if (LineFullMatch (L, "\tjsr\tstaxspp")) {
3910 /* We know about this function */
3912 } else if (LineFullMatch (L, "\tjsr\tstax0sp")) {
3913 /* We know about this function */
3915 } else if (LineFullMatch (L, "\tjsr\tstaxysp")) {
3916 /* We know about this function */
3918 } else if (LineFullMatch (L, "\tjsr\tsubeq0sp")) {
3919 /* We know about this function */
3922 } else if (LineFullMatch (L, "\tjsr\tsubeqysp")) {
3923 /* We know about this function */
3926 } else if (LineFullMatch (L, "\tjsr\ttosadda0")) {
3927 /* We know about this function */
3930 } else if (LineFullMatch (L, "\tjsr\ttosaddax")) {
3931 /* We know about this function */
3934 } else if (LineFullMatch (L, "\tjsr\ttosicmp")) {
3935 /* We know about this function */
3938 } else if (LineFullMatchX (L, Compares) >= 0) {
3941 } else if (LineFullMatchX (L, MakeBool) >= 0) {
3944 } else if (LineMatch (L, "\tjsr\t")) {
3945 /* Subroutine call, forget all register information */
3947 } else if (LineMatch (L, "\tlda\t")) {
3948 if (!RegAUsed (L) && !IsCondJump (NextInstruction (L))) {
3949 /* The value loaded is not used later, remove it */
3951 } else if (LineMatch (L, "\tlda\t(")) {
3952 if (IsXIndAddrMode (L)) {
3953 /* lda (zp,x) - if Y and X are both zero, replace by
3954 * load indirect y and save one cycle in some cases.
3956 if (X == 0 && Y == 0) {
3958 const char* S = L->Line + 6;
3960 strcpy (Buf, "\tlda\t(");
3968 L = ReplaceLine (L, Buf);
3971 /* In any case invalidate A */
3973 } else if (LineMatch (L, "\tlda\t#$")) {
3974 /* Immidiate load into A */
3975 NewVal = GetHexNum (L->Line + 7);
3977 /* Load has no effect */
3979 } else if (NewVal == X) {
3980 /* Requested value is already in X */
3981 L = ReplaceLine (L, "\ttxa");
3982 } else if (NewVal == Y) {
3983 /* Requested value is already in Y */
3984 L = ReplaceLine (L, "\ttya");
3986 /* Anyway, the new value is now in A */
3989 /* Memory load into A */
3992 } else if (LineMatch (L, "\tldax\t")) {
3993 /* Memory load into A and X */
3995 } else if (LineMatch (L, "\tldx\t")) {
3996 if (!RegXUsed (L) && !IsCondJump (NextInstruction (L))) {
3997 /* The value loaded is not used later, remove it */
3999 } else if (LineMatch (L, "\tldx\t#$")) {
4000 /* Immidiate load into X */
4001 NewVal = GetHexNum (L->Line + 7);
4003 /* Load has no effect */
4005 } else if (NewVal == A) {
4006 /* Requested value is already in A */
4007 L = ReplaceLine (L, "\ttax");
4008 } else if (X != -1 && NewVal == ((X + 1) & 0xFF)) {
4009 /* Requested value is one more than current contents */
4010 L = ReplaceLine (L, "\tinx");
4011 } else if (X != -1 && NewVal == ((X - 1) & 0xFF)) {
4012 /* Requested value is one less than current contents */
4013 L = ReplaceLine (L, "\tdex");
4015 /* Anyway, the new value is now in X */
4018 /* Memory load into X */
4021 } else if (LineMatch (L, "\tldy\t")) {
4022 if (!RegYUsed (L) && !IsCondJump (NextInstruction (L))) {
4023 /* The value loaded is not used later, remove it */
4025 } else if (LineMatch (L, "\tldy\t#$")) {
4026 /* Immidiate load into Y */
4027 NewVal = GetHexNum (L->Line + 7);
4029 /* Load has no effect */
4031 } else if (NewVal == A) {
4032 /* Requested value is already in A */
4033 L = ReplaceLine (L, "\ttay");
4034 } else if (Y != -1 && NewVal == ((Y + 1) & 0xFF)) {
4035 /* Requested value is one more than current contents */
4036 L = ReplaceLine (L, "\tiny");
4037 } else if (Y != -1 && NewVal == ((Y - 1) & 0xFF)) {
4038 /* Requested value is one less than current contents */
4039 L = ReplaceLine (L, "\tdey");
4041 /* Anyway, the new value is now in Y */
4044 /* Memory load into Y */
4047 } else if (LineFullMatch (L, "\tlsr\ta")) {
4051 } else if (LineMatch (L, "\tora\t#$")) {
4053 A |= GetHexNum (L->Line + 7);
4055 } else if (LineMatch (L, "\tora\t")) {
4057 } else if (LineFullMatch (L, "\tpla")) {
4059 } else if (LineFullMatch (L, "\trol\ta")) {
4061 } else if (LineFullMatch (L, "\tror\ta")) {
4063 } else if (LineFullMatch (L, "\trts")) {
4065 } else if (LineFullMatch (L, "\trti")) {
4067 } else if (LineMatch (L, "\tsbc\t")) {
4069 } else if (LineFullMatch (L, "\ttax")) {
4070 if (A != -1 && X == A) {
4071 /* Load has no effect */
4076 } else if (LineFullMatch (L, "\ttay")) {
4077 if (A != -1 && Y == A) {
4078 /* Load has no effect */
4083 } else if (LineFullMatch (L, "\ttsx")) {
4085 } else if (LineFullMatch (L, "\ttxa")) {
4086 if (X != -1 && A == X) {
4087 /* Load has no effect */
4092 } else if (LineFullMatch (L, "\ttya")) {
4093 if (Y != -1 && A == Y) {
4094 /* Load has no effect */
4101 /* Set to next line, handle deletions */
4102 L2 = NextCodeSegLine (L);
4110 /* Skip the label */
4111 L = NextCodeSegLine (L);
4118 static void OptBlocks (void)
4119 /* Optimize the register contents inside basic blocks */
4121 Line* L = FirstCode;
4123 L = OptOneBlock (L);
4129 static void OptJumps (void)
4130 /* Optimize jumps */
4132 static const char* Jumps [] = {
4142 Line* L = FirstCode;
4144 int I = LineMatchX (L, Jumps);
4146 Line* Target = GetTargetLine (L->Line+5);
4147 if (Target->Index > L->Index) {
4148 /* This is a forward jump. Backward jumps are handled
4149 * automagically by the assembler.
4151 unsigned Distance = GetJumpDistance (L, Target);
4152 if (Distance < 123) { /* Safety */
4153 L->Line [1] = 'b'; /* Make a short branch */
4154 L->Size = 2; /* Set new size */
4158 L = NextCodeLine (L);
4164 static void OptRTS (void)
4165 /* Change sequences of jsr XXX/rts to jmp XXX */
4167 Line* L = FirstCode;
4169 if (LineMatch (L, "\tjsr\t")) {
4170 /* This is a jsr, get the next instruction */
4171 Line* L2 = NextCodeLine (L);
4172 if (L2 && LineFullMatch (L2, "\trts")) {
4173 /* We found a sequence */
4179 /* Try the next line */
4180 L = NextCodeLine (L);
4186 /*****************************************************************************/
4188 /*****************************************************************************/
4192 static void OptOnePass (unsigned long Flag, void (*F) (void))
4193 /* Call one optimizer pass if enabled */
4195 if ((OptDisable & Flag) == 0) {
4197 } else if (Verbose || Debug) {
4198 printf ("Optimizer pass %04lX skipped\n", Flag);
4204 void OptDoOpt (void)
4205 /* Run the optimizer over the collected stuff */
4207 /* Find and remember the first line of code */
4210 /* Mark all lines inside the code segment */
4213 /* Create a list of all local labels for fast access */
4216 /* Ok, now start the real optimizations */
4218 /* Optimize compares - first step */
4219 OptOnePass (0x0001, OptCompares1);
4221 /* Remove dead jumps */
4222 OptOnePass (0x0002, OptDeadJumps);
4224 /* Remove unnecessary loads */
4225 OptOnePass (0x0004, OptLoads);
4227 /* Remove unnecessary register loads */
4228 OptOnePass (0x0008, OptRegLoads);
4230 /* Optimize stores through pointers */
4231 OptOnePass (0x0010, OptPtrOps);
4233 /* Optimize use of register variables */
4234 OptOnePass (0x0020, OptRegVars);
4236 /* Remove jump cascades - must be used before OptNeg */
4237 OptOnePass (0x0040, OptDoubleJumps);
4239 /* Remove unnecessary boolean negates */
4240 OptOnePass (0x0080, OptNeg);
4242 /* Replace jumps to an RTS by an RTS */
4243 OptOnePass (0x0100, OptJumpRTS);
4245 /* Optimize boolean transforms */
4246 OptOnePass (0x0200, OptBoolTransforms);
4248 /* Optimize compares */
4249 OptOnePass (0x0400, OptCompares2);
4251 /* Remove unnecessary tests */
4252 OptOnePass (0x0800, OptTests);
4254 /* Optimize several triples */
4255 OptOnePass (0x1000, OptTriples);
4257 /* Optimize basic blocks */
4258 OptOnePass (0x2000, OptBlocks);
4260 /* Remove unnecessary register loads (another pass) */
4261 OptOnePass (0x0008, OptRegLoads);
4263 /* Optimize jumps */
4264 OptOnePass (0x4000, OptJumps);
4266 /* Optimize jsr/rts sequences */
4267 OptOnePass (0x8000, OptRTS);