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 /*****************************************************************************/
56 /*****************************************************************************/
58 /*****************************************************************************/
62 /* Bitset of flags that switch the different optimizer passes */
63 unsigned long OptDisable = 0;
67 /* Bitmapped flags for the Flags field in the Line struct */
68 #define OF_CODE 0x0001 /* This line is in a code segment */
70 /* Pointer to first code line */
71 static Line* FirstCode;
74 static Line** Labels = 0; /* Pointers to label lines */
75 static unsigned LabelCount = 0; /* Count of local labels found */
77 /* A collection of lines */
78 typedef struct LineColl_ LineColl;
80 unsigned Count; /* Count of lines in the collection */
81 unsigned Max; /* Maximum count of lines */
82 Line* Lines[1]; /* Lines, dynamically allocated */
87 /* Calculate the element count of a table */
88 #define COUNT(T) (sizeof (T) / sizeof (T [0]))
90 /* Macro to increment and decrement register contents if they're valid */
91 #define INC(reg,val) if ((reg) >= 0) (reg) = ((reg) + val) & 0xFF
92 #define DEC(reg,val) if ((reg) >= 0) (reg) = ((reg) - val) & 0xFF
94 /* Defines for the conditions in a compare */
106 /* Defines for registers */
107 #define REG_NONE 0x00
111 #define REG_AX (REG_A | REG_X)
112 #define REG_ALL (REG_A | REG_X | REG_Y)
114 /* Description of the commands */
115 static const struct {
116 const char* Insn; /* Instruction */
117 unsigned char FullMatch; /* Match full instuction? */
118 unsigned char Use; /* Registers used */
119 unsigned char Load; /* Registers loaded */
121 { "\tadc\t", 0, REG_A, REG_NONE },
122 { "\tand\t", 0, REG_A, REG_NONE },
123 { "\tasl\ta", 1, REG_A, REG_NONE },
124 { "\tasl\t", 0, REG_NONE, REG_NONE },
125 { "\tclc", 1, REG_NONE, REG_NONE },
126 { "\tcld", 1, REG_NONE, REG_NONE },
127 { "\tcli", 1, REG_NONE, REG_NONE },
128 { "\tcmp\t", 0, REG_A, REG_NONE },
129 { "\tcpx\t", 0, REG_X, REG_NONE },
130 { "\tcpy\t", 0, REG_Y, REG_NONE },
131 { "\tdea", 1, REG_A, REG_NONE },
132 { "\tdec\ta", 1, REG_A, REG_NONE },
133 { "\tdec\t", 0, REG_NONE, REG_NONE },
134 { "\tdex", 1, REG_X, REG_NONE },
135 { "\tdey", 1, REG_Y, REG_NONE },
136 { "\teor\t", 0, REG_A, REG_NONE },
137 { "\tina", 1, REG_A, REG_NONE },
138 { "\tinc\ta", 1, REG_A, REG_NONE },
139 { "\tinc\t", 0, REG_NONE, REG_NONE },
140 { "\tinx", 1, REG_X, REG_NONE },
141 { "\tiny", 1, REG_Y, REG_NONE },
142 { "\tjsr\tbool", 0, REG_NONE, REG_AX },
143 { "\tjsr\tdecaxy", 1, REG_ALL, REG_AX },
144 { "\tjsr\tdecax", 0, REG_AX, REG_AX },
145 { "\tjsr\tldax0sp", 1, REG_Y, REG_AX },
146 { "\tjsr\tldaxysp", 1, REG_Y, REG_AX },
147 { "\tjsr\tpusha", 1, REG_A, REG_Y },
148 { "\tjsr\tpusha0", 1, REG_A, REG_X | REG_Y },
149 { "\tjsr\tpushax", 1, REG_AX, REG_Y },
150 { "\tjsr\tpushw0sp", 1, REG_NONE, REG_ALL },
151 { "\tjsr\tpushwysp", 1, REG_Y, REG_ALL },
152 { "\tjsr\ttosicmp", 1, REG_AX, REG_ALL },
153 { "\tlda\t", 0, REG_NONE, REG_A },
154 { "\tldax\t", 0, REG_NONE, REG_AX },
155 { "\tldx\t", 0, REG_NONE, REG_X },
156 { "\tldy\t", 0, REG_NONE, REG_Y },
157 { "\tlsr\ta", 1, REG_A, REG_NONE },
158 { "\tlsr\t", 0, REG_NONE, REG_NONE },
159 { "\tnop", 1, REG_NONE, REG_NONE },
160 { "\tora\t", 0, REG_A, REG_NONE },
161 { "\tpha", 1, REG_A, REG_NONE },
162 { "\tphp", 1, REG_NONE, REG_NONE },
163 { "\tpla", 1, REG_NONE, REG_A },
164 { "\tplp", 1, REG_NONE, REG_NONE },
165 { "\trol\ta", 1, REG_A, REG_A },
166 { "\trol\t", 0, REG_NONE, REG_NONE },
167 { "\tror\ta", 1, REG_A, REG_A },
168 { "\tror\t", 0, REG_NONE, REG_NONE },
169 { "\tsbc\t", 0, REG_A, REG_NONE },
170 { "\tsec", 1, REG_NONE, REG_NONE },
171 { "\tsed", 1, REG_NONE, REG_NONE },
172 { "\tsei", 1, REG_NONE, REG_NONE },
173 { "\tsta\t", 0, REG_A, REG_NONE },
174 { "\tstx\t", 0, REG_X, REG_NONE },
175 { "\tsty\t", 0, REG_Y, REG_NONE },
176 { "\tstz\t", 0, REG_NONE, REG_NONE },
177 { "\ttax", 1, REG_A, REG_X },
178 { "\ttay", 1, REG_A, REG_Y },
179 { "\ttsx", 1, REG_NONE, REG_X },
180 { "\ttxa", 1, REG_X, REG_A },
181 { "\ttya", 1, REG_Y, REG_A },
186 /* Table with the compare suffixes */
187 static const char CmpSuffixTab [][4] = {
188 "eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule"
191 /* Table used to invert a condition, indexed by condition */
192 static const unsigned char CmpInvertTab [] = {
194 CMP_LE, CMP_LT, CMP_GE, CMP_GT,
195 CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT
198 /* Table to show which compares are signed (use the N flag) */
199 static const char CmpSignedTab [] = {
200 0, 0, 1, 1, 1, 1, 0, 0, 0, 0
205 /* Lists of branches */
206 static const char* ShortBranches [] = {
217 static const char* LongBranches [] = {
231 /*****************************************************************************/
233 /*****************************************************************************/
237 static unsigned EstimateSize (Line* L);
238 /* Estimate the size of an instruction */
240 static int IsLocalLabel (const Line* L);
241 /* Return true if the line is a local label line */
243 static unsigned GetLabelNum (const char* L);
244 /* Return the label number of a label line */
246 static unsigned RVUInt1 (Line* L, LineColl* LC, unsigned Used, unsigned Unused);
247 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
249 static Line* NewLineAfter (Line* LineBefore, const char* Format, ...) attribute ((format(printf,2,3)));
250 /* Create a new line, insert it after L and return it. The new line is marked
254 static Line* ReplaceLine (Line* L, const char* Format, ...)
255 attribute ((format(printf,2,3)));
256 /* Replace one line by another */
260 /*****************************************************************************/
262 /*****************************************************************************/
266 static Line* NewLineAfter (Line* LineBefore, const char* Format, ...)
267 /* Create a new line, insert it after L and return it. The new line is marked
273 /* Format the new line and add it */
275 va_start (ap, Format);
276 L = NewCodeLineAfter (LineBefore, Format, ap);
279 /* Make the line a code line */
282 /* Estimate the code size */
283 L->Size = EstimateSize (L);
285 /* Return the new line */
291 static Line* NewLabelAfter (Line* L, unsigned Label)
292 /* Add a new line with a definition of a local label after the line L */
296 /* Create the label */
297 sprintf (Buf, "L%04X:", Label);
299 /* Create a new line */
300 L = NewLineAfter (L, Buf);
302 /* Insert this label into the label list */
305 /* Return the new line */
311 static void FreeLine (Line* L)
312 /* Remove a line from the list and free it */
314 /* If this is a label line, remove it from the label list */
315 if (IsLocalLabel (L)) {
316 Labels [GetLabelNum (L->Line)] = 0;
319 /* Unlink the line */
325 static Line* ReplaceLine (Line* L, const char* Format, ...)
326 /* Replace one line by another */
331 /* Format the new line */
333 va_start (ap, Format);
334 vsprintf (S, Format, ap);
337 /* Get the length of the new line */
340 /* We can copy the line if the old line has space enough */
343 /* Just copy the new line, but don't update the length */
344 memcpy (L->Line, S, Len);
345 L->Line [Len] = '\0';
349 /* We must allocate new space */
350 Line* NewLine = xmalloc (sizeof (Line) + Len);
352 /* Set the values in the new struct */
353 NewLine->Flags = L->Flags;
354 NewLine->Index = L->Index;
355 NewLine->Size = L->Size; /* Hmm ... */
357 memcpy (NewLine->Line, S, Len + 1);
359 /* Replace the old struct in the list */
360 NewLine->Next = L->Next;
362 NewLine->Next->Prev = NewLine;
367 NewLine->Prev = L->Prev;
369 NewLine->Prev->Next = NewLine;
375 /* Free the old struct */
380 /* Estimate the new size */
381 if (L->Flags & OF_CODE) {
382 L->Size = EstimateSize (L);
385 /* Return the line */
391 static Line* PrevCodeLine (Line* L)
392 /* Return the previous line containing code */
396 if (L->Flags & OF_CODE && L->Line [0] != '+') {
406 static Line* NextCodeSegLine (Line* L)
407 /* Return the next line in the code segment */
411 if (L->Flags & OF_CODE) {
421 static Line* NextCodeLine (Line* L)
422 /* Return the next line containing code */
426 if ((L->Flags & OF_CODE) != 0 && L->Line [0] != '+') {
436 static Line* NextInstruction (Line* L)
437 /* Return the next line containing code, ignoring labels. */
440 L = NextCodeLine (L);
441 } while (L && (L->Line[0] == '+' || IsLocalLabel(L)));
447 static void FreeLines (Line* Start, Line* End)
448 /* Delete all lines from Start to End, both inclusive */
453 Start = NextCodeSegLine (Start);
460 /*****************************************************************************/
461 /* Line Collections */
462 /*****************************************************************************/
466 static LineColl* NewLineColl (unsigned Size)
467 /* Create a new line collection and return it */
469 /* Allocate memory */
470 LineColl* LC = xmalloc (sizeof (LineColl) + sizeof (Line) * (Size-1));
472 /* Initialize members */
476 /* Return the new collection */
482 static void FreeLineColl (LineColl* LC)
483 /* Delete a line collection */
490 static int LCAddLine (LineColl* LC, Line* L)
491 /* Add a line. Return 0 if no space available, return 1 otherwise */
493 /* Check if there is enough space available */
494 if (LC->Count >= LC->Max) {
495 /* No room available */
500 LC->Lines [LC->Count++] = L;
508 static int LCHasLine (LineColl* LC, Line* L)
509 /* Check if the given line is in the collection */
512 for (I = 0; I < LC->Count; ++I) {
513 if (LC->Lines[I] == L) {
522 /*****************************************************************************/
523 /* Test a line for several things */
524 /*****************************************************************************/
528 static int IsLocalLabel (const Line* L)
529 /* Return true if the line is a local label line */
531 return (L->Line [0] == 'L' && isxdigit (L->Line [1]));
536 static int IsLabel (const Line* L)
537 /* Return true if the line is a label line */
539 return (L->Line [0] == 'L' && isxdigit (L->Line [1])) ||
540 (L->Line [0] == '_');;
545 static int IsHintLine (const Line* L)
546 /* Return true if the line contains an optimizer hint */
548 return L->Line [0] == '+';
553 static int IsSegHint (const Line* L)
554 /* Return true if the given line contains a segment hint */
556 return (L->Line [0] == '+' && strncmp (L->Line + 1, "seg:", 4) == 0);
561 static int IsHint (const Line* L, const char* Hint)
562 /* Check if the line contains a given hint */
564 return (L->Line [0] == '+' && strcmp (L->Line + 1, Hint) == 0);
569 static int IsCondJump (Line* L)
570 /* Return true if the line contains a conditional jump */
572 return (L->Line [0] == '\t' &&
573 (strncmp (L->Line + 1, "beq\t", 4) == 0 ||
574 strncmp (L->Line + 1, "bne\t", 4) == 0 ||
575 strncmp (L->Line + 1, "jeq\t", 4) == 0 ||
576 strncmp (L->Line + 1, "jne\t", 4) == 0));
581 static int IsXAddrMode (Line* L)
582 /* Return true if the given line does use the X register */
584 unsigned Len = strlen (L->Line);
585 return (strcmp (L->Line + Len - 3, ",x)") == 0 ||
586 strcmp (L->Line + Len - 2, ",x") == 0);
591 static int NoXAddrMode (Line* L)
592 /* Return true if the given line does use the X register */
594 return !IsXAddrMode (L);
599 static int IsYAddrMode (Line* L)
600 /* Return true if the given line does use the Y register */
602 unsigned Len = strlen (L->Line);
603 return (strcmp (L->Line + Len - 2, ",y") == 0);
608 static Line* FindHint (Line* L, const char* Hint)
609 /* Search for a line with the given hint */
612 if (IsHint (L, Hint)) {
622 static unsigned GetHexNum (const char* S)
623 /* Get a hex number from a string */
627 while (isxdigit (S [I])) {
628 int C = (unsigned char) (S [I++]);
634 Val = (Val << 4) + C;
641 static unsigned GetLabelNum (const char* L)
642 /* Return the label number of a label line */
645 return GetHexNum (L+1);
650 static Line* GetTargetLine (const char* L)
651 /* Get the line with the target label of a jump. L must be a pointer to the
652 * string containing the label number.
657 /* Get the label number of the target */
658 unsigned Label = GetLabelNum (L);
659 CHECK (Label < LabelCount);
661 /* Get the line with this label */
662 Target = Labels [Label];
663 CHECK (Target != 0 && (Target->Flags & OF_CODE) != 0);
671 static unsigned GetJumpDistance (Line* L, Line* Target)
672 /* Get the distance between both lines */
674 unsigned Distance = 0;
677 if (Target->Index > L->Index) {
678 /* This is a forward jump. */
680 L = NextCodeLine (L);
682 } while (L != Target);
684 /* This is a backward jump */
686 L = PrevCodeLine (L);
688 } while (L != Target);
692 /* Return the calculated distance */
698 static int LineMatch (const Line* L, const char* Start)
699 /* Check if the start of the line matches Start */
701 return strncmp (L->Line, Start, strlen (Start)) == 0;
706 static int LineFullMatch (const Line* L, const char* Start)
707 /* Check if the matches Start */
709 return strcmp (L->Line, Start) == 0;
714 static int LineMatchX (const Line* L, const char** Start)
715 /* Check the start of the line against a list of patterns. Return the
716 * number of the pattern that matched, or -1 in case of no match.
721 if (LineMatch (L, *Start)) {
734 static int LineFullMatchX (const Line* L, const char** Start)
735 /* Check the the line against a list of patterns. Return the
736 * number of the pattern that matched, or -1 in case of no match.
741 if (LineFullMatch (L, *Start)) {
754 static int IsLoadAX (Line* L1, Line* L2)
755 /* Check if the both lines load a static variable into ax. That is, both lines
761 return LineMatch (L1, "\tlda\t") &&
762 LineMatch (L2, "\tldx\t") &&
763 strncmp (L1->Line+5, L2->Line+5, strlen (L1->Line+5)) == 0 &&
764 strcmp (L2->Line+strlen(L1->Line), "+1") == 0;
769 /*****************************************************************************/
770 /* Initial optimizer setup */
771 /*****************************************************************************/
775 static void FindCodeStart (void)
776 /* Find and remember the first line of actual code */
778 Line* L = FindHint (FirstLine, "end_of_preamble");
779 FirstCode = L? L->Next : 0;
784 static unsigned EstimateDataSize (Line* L, unsigned Chunk)
785 /* Estimate the size of a .byte, .word or .dword command */
787 unsigned Size = Chunk;
789 while ((S = strchr (S, ',')) != 0) {
798 static unsigned EstimateSize (Line* L)
799 /* Estimate the size of an instruction */
801 static const char* OneByteCmds [] = {
817 if (L->Line [0] != '\t') {
820 if (LineMatch (L, "\tldax\t")) {
821 /* Immidiate load of both, A and X */
824 if (LineMatch (L, "\tld")) {
825 OpStart = L->Line [5];
826 return (OpStart == '#' || OpStart == '(')? 2 : 3;
828 if (LineMatch (L, "\tst")) {
829 OpStart = L->Line [5];
830 return (OpStart == '(')? 2 : 3;
832 if (LineMatch (L, "\t.byte\t")) {
833 return EstimateDataSize (L, 1);
835 if (LineMatch (L, "\t.word\t")) {
836 return EstimateDataSize (L, 2);
838 if (LineMatch (L, "\t.dword\t")) {
839 return EstimateDataSize (L, 4);
841 if (LineMatchX (L, ShortBranches) >= 0) {
844 if (LineMatchX (L, LongBranches) >= 0) {
847 if (LineMatchX (L, OneByteCmds) >= 0) {
855 static void MarkCodeLines (void)
856 /* Mark all lines that are inside a code segment */
862 InCode = IsHint (L, "seg:code");
863 } else if (InCode && L->Line[0] != '\0') {
865 L->Size = EstimateSize (L);
873 static void CreateLabelList (void)
874 /* Create a list with pointers to local labels */
880 /* Get the next label number. This is also the current label count.
881 * Make some room for more labels when optimizing code.
883 LabelCount = GetLabel () + 100;
885 /* Allocate memory for the array and clear it */
886 Labels = xmalloc (LabelCount * sizeof (Line*));
887 for (I = 0; I < LabelCount; ++I) {
891 /* Walk through the code and insert all label lines */
894 if (IsLocalLabel (L)) {
895 unsigned LabelNum = GetLabelNum (L->Line);
896 CHECK (LabelNum < LabelCount);
897 Labels [LabelNum] = L;
905 static unsigned AllocLabel (void)
906 /* Get a new label. The current code does not realloc the label list, so there
907 * must be room enough in the current list.
912 /* Search for a free slot, start at 1, since 0 is "no label" */
913 for (I = 1; I < LabelCount; ++I) {
914 if (Labels[I] == 0) {
915 /* Found a free slot */
920 /* No label space available */
921 Internal ("Out of label space in the optimizer");
929 /*****************************************************************************/
930 /* Helper functions */
931 /*****************************************************************************/
935 static int GetNextCodeLines (Line* L, Line** Lines, unsigned Count)
936 /* Get a number of code lines ignoring hints and other stuff. The function
937 * returns 1 if we got the lines and 0 if we are at the end of the code
938 * segment or if we hit a label.
943 /* Get the next valid line */
945 L = NextCodeLine (L);
946 } while (L && IsHintLine (L));
948 /* Did we get one? */
949 if (L == 0 || IsLabel (L)) {
954 /* Remember the line */
964 static int FindCond (const char* Suffix)
965 /* Map a condition suffix to a code. Return the code or -1 on failure */
970 for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) {
971 if (strncmp (Suffix, CmpSuffixTab [I], strlen (CmpSuffixTab[I])) == 0) {
983 static int CheckAndGetIntCmp (const Line* JSR, const Line* JMP)
984 /* Helper function to check for a compare subroutine call followed by a
985 * conditional branch. Will return the condition found, or -1 if no
986 * or invalid condition.
993 /* Extract the condition from the function name. */
994 if ((Cond [0] = JSR->Line [8]) == 'u') {
995 Cond [1] = JSR->Line [9];
996 Cond [2] = JSR->Line [10];
998 Tail = JSR->Line + 11;
1000 Cond [1] = JSR->Line [9];
1002 Tail = JSR->Line + 10;
1005 /* Check if this is indeed an integer function */
1006 if (strcmp (Tail, "ax") != 0) {
1011 /* Get the condition code */
1012 C = FindCond (Cond);
1018 /* Invert the code if we jump on condition not met. */
1019 if (JMP->Line [2] == 'e' && JMP->Line [3] == 'q') {
1020 /* Jumps if condition false, invert condition */
1021 C = CmpInvertTab [C];
1024 /* Return the condition code */
1030 static int TosCmpFunc (Line* L)
1031 /* Check if this is a call to one of the TOS compare functions (tosgtax).
1032 * Return the condition code or -1 if not found.
1035 if (LineMatch (L, "\tjsr\ttos") &&
1036 strcmp (L->Line+strlen(L->Line)-2, "ax") == 0) {
1038 /* Ok, found. Get the condition. */
1039 return FindCond (L->Line+8);
1050 static int IsUnsignedCmp (int Code)
1051 /* Check if this is an unsigned compare */
1054 return CmpSignedTab [Code] == 0;
1059 static void InvertZJump (Line* L)
1060 /* Invert a jeq/jne jump */
1062 if (L->Line [2] == 'n' && L->Line [3] == 'e') {
1063 /* This was a bne/jne */
1067 /* This was (hopefully) a beq/jeq */
1075 static int FindCmd (Line* L)
1079 /* Search for the known patterns */
1080 for (I = 0; I < COUNT(CmdDesc); ++I) {
1081 if (CmdDesc[I].FullMatch) {
1082 if (LineFullMatch (L, CmdDesc[I].Insn)) {
1087 if (LineMatch (L, CmdDesc[I].Insn)) {
1099 static unsigned RVUInt2 (Line* L,
1100 LineColl* LC, /* To remember visited lines */
1101 unsigned Used, /* Definitely used registers */
1102 unsigned Unused) /* Definitely unused registers */
1103 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
1107 /* Check the following instructions. We classifiy them into primary
1108 * loads (register value not used), neutral (check next instruction),
1109 * and unknown (assume register was used).
1115 /* Get the next line and follow jumps */
1118 /* Handle jumps to local labels (continue there) */
1119 if (LineMatch (L, "\tjmp\tL") || LineMatch (L, "\tbra\tL")) {
1120 /* Get the target of the jump */
1121 L = GetTargetLine (L->Line+5);
1124 /* Get the next instruction line */
1125 L = NextInstruction (L);
1127 /* Bail out if we're done */
1128 if (L == 0 || IsLabel (L)) {
1129 /* Something is wrong */
1133 /* Check if we had this line already. If so, bail out, if not,
1134 * add it to the list of known lines.
1136 if (LCHasLine (LC, L) || !LCAddLine (LC, L)) {
1140 } while (LineMatch (L, "\tjmp\tL") || LineMatch (L, "\tbra\tL"));
1142 /* Special handling for branches */
1143 if (LineMatchX (L, ShortBranches) >= 0 ||
1144 LineMatchX (L, LongBranches) >= 0) {
1145 const char* Target = L->Line+5;
1146 if (Target[0] == 'L') {
1147 /* Jump to local label. Check the register usage starting at
1148 * the branch target and at the code following the branch.
1149 * All registers that are unused in both execution flows are
1150 * returned as unused.
1153 U2 = RVUInt1 (GetTargetLine (Target), LC, Used, Unused);
1154 U1 = RVUInt1 (L, LC, Used, Unused);
1155 return U1 | U2; /* Used in any of the branches */
1159 /* Search for the instruction in this line */
1162 /* If we don't find it, assume all other registers are */
1167 /* Evaluate the use flags, check for addressing modes */
1169 if (IsXAddrMode (L)) {
1171 } else if (IsYAddrMode (L)) {
1175 /* Remove registers that were already new loaded */
1178 /* Remember the remaining registers */
1182 /* Evaluate the load flags */
1183 R = CmdDesc[I].Load;
1185 /* Remove registers that were already used */
1188 /* Remember the remaining registers */
1192 /* If we know about all registers, bail out */
1193 if ((Used | Unused) == REG_ALL) {
1199 /* Return to the caller the complement of all unused registers */
1200 return ~Unused & REG_ALL;
1205 static unsigned RVUInt1 (Line* L,
1206 LineColl* LC, /* To remember visited lines */
1207 unsigned Used, /* Definitely used registers */
1208 unsigned Unused) /* Definitely unused registers */
1209 /* Subfunction for RegValUsed. Will be called recursively in case of branches. */
1211 /* Remember the current count of the line collection */
1212 unsigned Count = LC->Count;
1214 /* Call the worker routine */
1215 unsigned R = RVUInt2 (L, LC, Used, Unused);
1217 /* Restore the old count */
1220 /* Return the result */
1226 static unsigned RegValUsed (Line* Start)
1227 /* Check the next instructions after the one in L for register usage. If
1228 * a register is used as an index, or in a store or other instruction, it
1229 * is assumed to be used. If a register is loaded with a value, before it
1230 * was used by one of the actions described above, it is assumed unused.
1231 * If the end of the lookahead is reached, all registers that are uncertain
1232 * are marked as used.
1233 * The result of the search is returned.
1238 /* Create a new line collection and enter the start line */
1239 LineColl* LC = NewLineColl (256);
1240 LCAddLine (LC, Start);
1242 /* Call the recursive subfunction */
1243 R = RVUInt1 (Start, LC, REG_NONE, REG_NONE);
1245 /* Delete the line collection */
1248 /* Return the registers used */
1254 static int RegAUsed (Line* Start)
1255 /* Check if the value in A is used. */
1257 return (RegValUsed (Start) & REG_A) != 0;
1262 static int RegXUsed (Line* Start)
1263 /* Check if the value in X is used. */
1265 return (RegValUsed (Start) & REG_X) != 0;
1270 static int RegYUsed (Line* Start)
1271 /* Check if the value in Y is used. */
1273 return (RegValUsed (Start) & REG_Y) != 0;
1278 /*****************************************************************************/
1279 /* Real optimizer routines */
1280 /*****************************************************************************/
1284 static void OptCompares1 (void)
1285 /* Try to optimize the integer compare subroutines. */
1287 Line* L2[10]; /* Line lookahead */
1288 int Cond; /* Condition to evaluate */
1289 unsigned Label; /* Local label number */
1290 unsigned Offs; /* Stack offset */
1291 Line* DelStart; /* First line to delete */
1293 Line* L = FirstCode;
1296 /* Search for compares of local byte sized variables. This looks
1308 * Replace it by a direct compare:
1316 if (LineMatch (L, "\tldy\t#$") &&
1317 GetNextCodeLines (L, L2, 7) &&
1318 LineFullMatch (L2[0], "\tldx\t#$00") &&
1319 LineFullMatch (L2[1], "\tlda\t(sp),y") &&
1320 LineFullMatch (L2[2], "\tjsr\tpushax") &&
1321 LineMatch (L2[3], "\tldy\t#$") &&
1322 LineFullMatch (L2[4], "\tldx\t#$00") &&
1323 LineFullMatch (L2[5], "\tlda\t(sp),y") &&
1324 (Cond = TosCmpFunc (L2[6])) >= 0) {
1326 /* Get the stack offset and correct it, since we will remove
1329 Offs = GetHexNum (L2[3]->Line+7) - 2;
1332 L = NewLineAfter (L, "\tlda\t(sp),y");
1333 L = NewLineAfter (L, "\tldy\t#$%02X", Offs);
1334 L = NewLineAfter (L, "\tcmp\t(sp),y");
1335 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1337 /* Remove the old cruft */
1338 FreeLines (L2[0], L2[6]);
1341 /* Compares of byte sized global variables */
1342 else if (LineFullMatch (L, "\tldx\t#$00") &&
1343 GetNextCodeLines (L, L2, 5) &&
1344 LineMatch (L2[0], "\tlda\t") &&
1345 LineFullMatch (L2[1], "\tjsr\tpushax") &&
1346 LineFullMatch (L2[2], "\tldx\t#$00") &&
1347 LineMatch (L2[3], "\tlda\t") &&
1348 (Cond = TosCmpFunc (L2[4])) >= 0) {
1351 if (IsXAddrMode (L2[0])) {
1352 /* The load is X indirect, so we may not remove the load
1353 * of the X register.
1358 L = ReplaceLine (L, L2[0]->Line);
1361 L = NewLineAfter (L, "\tcmp\t%s", L2[3]->Line+5);
1362 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1364 /* Remove the old cruft */
1365 FreeLines (DelStart, L2[4]);
1369 /* Byte sized local to global */
1370 else if (LineMatch (L, "\tldy\t#$") &&
1371 GetNextCodeLines (L, L2, 6) &&
1372 LineFullMatch (L2[0], "\tldx\t#$00") &&
1373 LineFullMatch (L2[1], "\tlda\t(sp),y") &&
1374 LineFullMatch (L2[2], "\tjsr\tpushax") &&
1375 LineFullMatch (L2[3], "\tldx\t#$00") &&
1376 LineMatch (L2[4], "\tlda\t") &&
1377 (Cond = TosCmpFunc (L2[5])) >= 0) {
1380 L = NewLineAfter (L, L2[1]->Line);
1381 L = NewLineAfter (L, "\tcmp\t%s", L2[4]->Line+5);
1382 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1384 /* Remove the old cruft */
1385 FreeLines (L2[0], L2[5]);
1389 /* Byte sized global to local */
1390 else if (LineFullMatch (L, "\tldx\t#$00") &&
1391 GetNextCodeLines (L, L2, 6) &&
1392 LineMatch (L2[0], "\tlda\t") &&
1393 LineFullMatch (L2[1], "\tjsr\tpushax") &&
1394 LineMatch (L2[2], "\tldy\t#$") &&
1395 LineFullMatch (L2[3], "\tldx\t#$00") &&
1396 LineFullMatch (L2[4], "\tlda\t(sp),y") &&
1397 (Cond = TosCmpFunc (L2[5])) >= 0) {
1399 /* Get the stack offset and correct it, since we will remove
1402 Offs = GetHexNum (L2[2]->Line+7) - 2;
1405 if (IsXAddrMode (L2[0])) {
1406 /* The load is X indirect, so we may not remove the load
1407 * of the X register.
1412 L = ReplaceLine (L, L2[0]->Line);
1415 L = NewLineAfter (L, "\tldy\t#$%02X", Offs);
1416 L = NewLineAfter (L, "\tcmp\t(sp),y");
1417 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1419 /* Remove the old cruft */
1420 FreeLines (DelStart, L2[5]);
1424 /* Search for unsigned compares against global variables. This looks
1432 * Replace that by a direct compare:
1440 else if (LineFullMatch (L, "\tjsr\tpushax") &&
1441 GetNextCodeLines (L, L2, 3) &&
1442 IsLoadAX (L2[0], L2[1]) &&
1443 (Cond = TosCmpFunc (L2[2])) >= 0 &&
1444 IsUnsignedCmp (Cond)) {
1446 /* Get a free label number */
1447 Label = AllocLabel ();
1449 /* Replace the code */
1450 L = ReplaceLine (L, "\tcpx\t%s", L2[1]->Line+5);
1451 L = NewLineAfter (L, "\tbne\tL%04X", Label);
1452 L = NewLineAfter (L, "\tcmp\t%s", L2[0]->Line+5);
1453 L = NewLabelAfter(L, Label);
1454 L = NewLineAfter (L, "\tjsr\tbool%s", CmpSuffixTab[Cond]);
1456 /* Remove the old code */
1457 FreeLines (L2[0], L2[2]);
1461 L = NextCodeLine (L);
1467 static void OptDeadJumps (void)
1468 /* Remove jumps to the following instruction */
1470 static const char* Jumps [] = {
1479 Line* L = FirstCode;
1482 /* Get a pointer to the next instruction line */
1483 Line* NextLine = NextInstruction (L);
1485 /* Is this line a jump? */
1486 int I = LineMatchX (L, Jumps);
1488 /* Yes. Get the target label, skip labels */
1489 Line* Target = NextInstruction (GetTargetLine (L->Line+5));
1491 /* If the target label is the next line, remove the jump */
1492 if (Target == NextLine) {
1497 /* Go to the next line */
1504 static void OptLoads (void)
1505 /* Remove unnecessary loads of values */
1509 Line* L = FirstCode;
1530 * This change will cost 3 cycles (one additional jump inside the
1531 * subroutine), but it saves a lot of code (6 bytes per occurrence),
1532 * so we will accept the overhead. It may even be possible to rewrite
1533 * the library routine to get rid of the additional overhead.
1535 if (LineMatch (L, "\tldy\t#$") &&
1536 GetNextCodeLines (L, L2, 5) &&
1537 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
1538 LineFullMatch (L2 [1], "\ttax") &&
1539 LineFullMatch (L2 [2], "\tdey") &&
1540 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
1541 LineFullMatch (L2 [4], "\tjsr\tpushax")) {
1543 /* Found - replace it */
1544 if (LineFullMatch (L, "\tldy\t#$01")) {
1545 /* Word at offset zero */
1547 L = ReplaceLine (L2 [4], "\tjsr\tpushw0sp");
1549 ReplaceLine (L2 [4], "\tjsr\tpushwysp");
1552 /* Delete the remaining lines */
1553 FreeLines (L2 [0], L2 [3]);
1576 * This change will cost 2 cycles, but it saves a lot of code (6 bytes
1577 * per occurrence), so we will accept the overhead. It may even be
1578 * possible to rewrite the library routine to get rid of the additional
1581 if (LineMatch (L, "\tldy\t#$") &&
1582 GetNextCodeLines (L, L2, 6) &&
1583 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
1584 LineFullMatch (L2 [1], "\ttax") &&
1585 LineFullMatch (L2 [2], "\tdey") &&
1586 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
1587 LineMatch (L2 [4], "\tldy\t#$") &&
1588 LineFullMatch (L2 [5], "\tjsr\tldauidx")) {
1590 /* Found - replace it */
1591 L2 [4]->Line [3] = 'x'; /* Change to ldx */
1592 if (LineFullMatch (L, "\tldy\t#$01")) {
1593 /* Word at offset zero */
1595 L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp");
1597 ReplaceLine (L2 [5], "\tjsr\tldauiysp");
1600 /* Delete the remaining lines */
1601 FreeLines (L2 [0], L2 [3]);
1613 if (LineFullMatch (L, "\tlda\t(sp),y") &&
1614 GetNextCodeLines (L, L2, 1) &&
1615 LineFullMatch (L2 [0], "\tjsr\tpusha")) {
1617 /* Found, replace it */
1618 L = ReplaceLine (L, "\tjsr\tpushaysp");
1622 /* All other patterns start with this one: */
1623 if (!LineFullMatch (L, "\tldx\t#$00")) {
1633 * and replace it by:
1638 if (GetNextCodeLines (L, L2, 1) &&
1639 LineFullMatch (L2 [0], "\tjsr\tpushax")) {
1641 /* Replace the subroutine call */
1642 L = ReplaceLine (L, "\tjsr\tpusha0");
1644 /* Remove the unnecessary line */
1654 * and replace it by:
1660 else if (GetNextCodeLines (L, L2, 2) &&
1661 LineMatch (L2 [0], "\tlda\t") &&
1662 LineFullMatch (L2 [1], "\tjsr\tpushax")) {
1664 /* Be sure, X is not used in the load */
1665 if (NoXAddrMode (L2 [0])) {
1667 /* Replace the subroutine call */
1668 L2 [1] = ReplaceLine (L2 [1], "\tjsr\tpusha0");
1670 /* Remove the unnecessary load */
1673 /* L must be valid */
1685 * and replace it by:
1690 else if (GetNextCodeLines (L, L2, 2) &&
1691 LineMatch (L2 [0], "\tlda\t") &&
1692 LineMatch (L2 [1], "\tcmp\t#$")) {
1694 /* Be sure, X is not used in the load */
1695 if (NoXAddrMode (L2 [0])) {
1697 /* Remove the unnecessary load */
1700 /* L must be valid */
1711 * and replace it by:
1716 else if (GetNextCodeLines (L, L2, 2) &&
1717 LineMatch (L2 [0], "\tlda\t") &&
1718 LineFullMatch (L2 [1], "\tjsr\tbnega")) {
1720 /* Be sure, X is not used in the load */
1721 if (NoXAddrMode (L2 [0])) {
1723 /* Remove the unnecessary load */
1726 /* L must be valid */
1732 /* Go to the next line */
1733 L = NextCodeLine (L);
1739 static void OptRegLoads (void)
1740 /* Remove unnecessary loads of registers */
1746 /* Repeat this until there is nothing more to delete */
1754 /* Search for a load of X and check if the value is used later */
1755 if (LineMatch (L, "\tldx\t") &&
1757 !IsCondJump (NextInstruction (L))) {
1759 /* Remember to delete this line */
1763 /* Search for a load of A and check if the value is used later */
1764 else if (LineMatch (L, "\tlda\t") &&
1766 !IsCondJump (NextInstruction (L))) {
1768 /* Remember to delete this line */
1772 /* Search for a load of Y and check if the value is used later */
1773 else if (LineMatch (L, "\tldy\t") &&
1775 !IsCondJump (NextInstruction (L))) {
1777 /* Remember to delete this line */
1781 /* Go to the next line, delete the current if requested */
1783 L = NextCodeLine (L);
1789 } while (Deletions > 0);
1794 static int OptPtrOps1 (Line** Start)
1795 /* Optimize several pointer and array constructs - subfunction 1 */
1800 unsigned LinesToRemove;
1805 /* Use a local variable for the working line */
1808 /* Search for (23B/XXT)
1820 * and replace it by something like (24B/26T)
1834 if (!LineMatch (L, "\tlda\t") ||
1835 !GetNextCodeLines (L, L2, 4) ||
1836 !IsLoadAX (L, L2 [0]) ||
1837 !LineFullMatch (L2[1], "\tsta\tregsave") ||
1838 !LineFullMatch (L2[2], "\tstx\tregsave+1")) {
1845 if (LineMatch (L2[3], "\tjsr\tincax")) {
1846 /* Get next code lines */
1847 if (GetNextCodeLines (L2[3], &L2[4], 4) == 0) {
1848 /* Cannot get lines */
1851 Inc = GetHexNum (L2[3]->Line+10);
1855 /* Get next code lines */
1856 if (GetNextCodeLines (L2[3], &L2[4], 7) == 0) {
1857 /* Cannot get lines */
1860 if (LineFullMatch (L2[3], "\tclc") &&
1861 LineMatch (L2[4], "\tadc\t#$") &&
1862 LineFullMatch (L2[5], "\tbcc\t*+3") &&
1863 LineFullMatch (L2[6], "\tinx")) {
1864 /* Inlined increment */
1865 Inc = GetHexNum (L2[4]->Line+7);
1874 /* Check for the remainder */
1875 if (!LineMatch (L3[0], "\tsta\t") ||
1876 strcmp (L3[0]->Line+5, L->Line+5) != 0 ||
1877 !LineMatch (L3[1], "\tstx\t") ||
1878 strcmp (L3[1]->Line+5, L2[0]->Line+5) != 0 ||
1879 !LineFullMatch (L3[2], "\tlda\tregsave") ||
1880 !LineFullMatch (L3[3], "\tldx\tregsave+1")) {
1886 /* Check if AX is actually used following the code above. If not,
1887 * we don't need to load A/X from regsave. Since X will never be
1888 * used without A, check just for A.
1890 NeedLoad = RegAUsed (L3[3]);
1892 /* Special code for register variables */
1894 if (LineMatch (L, "\tlda\tregbank+") &&
1895 GetNextCodeLines (L3[3], &L3[4], 1) &&
1898 /* Remember the offset into the register bank */
1900 strcpy (Reg, L->Line+5);
1902 /* Check for several special sequences */
1903 if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
1904 /* Load char indirect */
1905 L = ReplaceLine (L, "\tldx\t#$00");
1906 L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
1907 L = NewLineAfter (L, "\tinc\t%s", Reg);
1908 L = NewLineAfter (L, "\tbne\t*+4");
1909 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1912 } else if (LineFullMatch (L3[4], "\tsta\tptr1") &&
1913 GetNextCodeLines (L3[4], &L3[5], 3) &&
1914 LineFullMatch (L3[5], "\tstx\tptr1+1") &&
1915 LineFullMatch (L3[6], "\tldx\t#$00") &&
1916 LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
1918 /* Load char indirect, inlined */
1919 L = ReplaceLine (L, "\tldx\t#$00");
1920 L = NewLineAfter (L, "\tlda\t(%s,x)", Reg);
1921 L = NewLineAfter (L, "\tinc\t%s", Reg);
1922 L = NewLineAfter (L, "\tbne\t*+4");
1923 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1927 } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
1928 if (GetNextCodeLines (L3[4], &L3[5], 2) &&
1929 LineMatch (L3[5], "\tlda\t") &&
1930 LineFullMatch (L3[6], "\tjsr\tstaspp")) {
1932 /* Store to pointer */
1933 L = ReplaceLine (L, L3[5]->Line);
1934 L = NewLineAfter (L, "\tldy\t#$00");
1935 L = NewLineAfter (L, "\tsta\t(%s),y", Reg);
1936 L = NewLineAfter (L, "\tinc\t%s", Reg);
1937 L = NewLineAfter (L, "\tbne\t*+4");
1938 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1943 } else if (GetNextCodeLines (L3[4], &L3[5], 3) &&
1944 LineMatch (L3[5], "\tldy\t#$") &&
1945 LineFullMatch (L3[6], "\tlda\t(sp),y") &&
1946 LineFullMatch (L3[7], "\tjsr\tstaspp")) {
1948 /* Beware: We have to correct the stack offset, since we will
1949 * remove the pushax instruction!
1951 Offs = GetHexNum (L3[5]->Line+7) - 2;
1953 /* Store to pointer */
1954 L = ReplaceLine (L, "\tldy\t#$%02X", Offs);
1955 L = NewLineAfter (L, "\tldx\t#$00");
1956 L = NewLineAfter (L, "\tlda\t(sp),y");
1957 L = NewLineAfter (L, "\tsta\t(%s,x)", Reg);
1958 L = NewLineAfter (L, "\tinc\t%s", Reg);
1959 L = NewLineAfter (L, "\tbne\t*+4");
1960 L = NewLineAfter (L, "\tinc\t%s+1", Reg);
1970 /* No register variable - insert the first part of the code */
1972 L = NewLineAfter (L, "\tsta\tptr1");
1974 L = NewLineAfter (L, "\tclc");
1975 L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
1976 L = NewLineAfter (L, "\tsta\t%s", L3[0]->Line+5);
1977 L = NewLineAfter (L, "\tlda\t%s", L3[1]->Line+5);
1979 L = NewLineAfter (L, "\tsta\tptr1+1");
1981 L = NewLineAfter (L, "\tadc\t#$00");
1982 L = NewLineAfter (L, "\tsta\t%s", L3[1]->Line+5);
1984 /* Check if we must really load the old value into a/x or if the
1985 * code may be replaced by something else.
1987 if (GetNextCodeLines (L3[3], &L3[4], 1)) {
1988 if (LineFullMatch (L3[4], "\tjsr\tldaui")) {
1989 /* Load char indirect */
1990 L = NewLineAfter (L, "\tldx\t#$00");
1991 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
1994 } else if (LineFullMatch (L3[4], "\tsta\tptr1") &&
1995 GetNextCodeLines (L3[4], &L3[5], 3) &&
1996 LineFullMatch (L3[5], "\tstx\tptr1+1") &&
1997 LineFullMatch (L3[6], "\tldx\t#$00") &&
1998 LineFullMatch (L3[7], "\tlda\t(ptr1,x)")) {
2000 /* Load char indirect, inlined */
2001 L = NewLineAfter (L, "\tldx\t#$00");
2002 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2006 } else if (LineFullMatch (L3[4], "\tjsr\tldaxi")) {
2007 /* Load word indirect */
2008 L = NewLineAfter (L, "\tldy\t#$01");
2009 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2010 L = NewLineAfter (L, "\ttax");
2011 L = NewLineAfter (L, "\tdey");
2012 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2016 } else if (LineFullMatch (L3[4], "\tjsr\tpushax")) {
2017 if (GetNextCodeLines (L3[4], &L3[5], 2) &&
2018 LineMatch (L3[5], "\tlda\t") &&
2019 LineFullMatch (L3[6], "\tjsr\tstaspp")) {
2021 /* Store to pointer */
2022 L = NewLineAfter (L, L3[5]->Line);
2023 L = NewLineAfter (L, "\tldy\t#$00");
2024 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2028 } else if (GetNextCodeLines (L3[4], &L3[5], 3) &&
2029 LineMatch (L3[5], "\tldy\t#$") &&
2030 LineFullMatch (L3[6], "\tlda\t(sp),y") &&
2031 LineFullMatch (L3[7], "\tjsr\tstaspp")) {
2033 /* Beware: We have to correct the stack offset, since we will
2034 * remove the pushax instruction!
2036 sprintf (L3[5]->Line+7, "%02X", GetHexNum (L3[5]->Line+7)-2);
2038 /* Store to pointer */
2039 L = NewLineAfter (L, L3[5]->Line);
2040 L = NewLineAfter (L, L3[6]->Line);
2041 L = NewLineAfter (L, "\tldy\t#$00");
2042 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2051 /* If we need to load a/x, add the code */
2053 L = NewLineAfter (L, "\tlda\tptr1");
2054 L = NewLineAfter (L, "\tldx\tptr1+1");
2058 /* Remove the code that is no longer needed */
2059 FreeLines (L2[0], L2[LinesToRemove-1]);
2061 /* Return the new line and success */
2062 *Start = NextCodeLine (L);
2068 static int OptPtrOps2 (Line** Start)
2069 /* Optimize several pointer and array constructs - subfunction 2 */
2074 unsigned LinesToRemove;
2079 /* Use a local variable for the working line */
2082 /* Same as subfunction 1 but for local variables. */
2083 if (LineMatch (L, "\tldy\t#$") == 0) {
2087 /* Get the stack offset. The offset points to the high byte, correct that. */
2088 Offs = GetHexNum (L->Line+7) - 1;
2090 /* Check for the actual sequences */
2091 if (GetNextCodeLines (L, L2, 7) &&
2092 LineFullMatch (L2[0], "\tjsr\tldaxysp") &&
2093 LineFullMatch (L2[1], "\tsta\tregsave") &&
2094 LineFullMatch (L2[2], "\tstx\tregsave+1") &&
2095 LineMatch (L2[3], "\tjsr\tincax")) {
2097 /* Non inlined version */
2098 Inc = GetHexNum (L2[3]->Line+10);
2100 /* Check for stack offset zero */
2101 if (LineFullMatch (L2[4], "\tjsr\tstax0sp") &&
2102 LineFullMatch (L2[5], "\tlda\tregsave") &&
2103 LineFullMatch (L2[6], "\tldx\tregsave+1")) {
2107 } else if (GetNextCodeLines (L2[6], &L2[7], 1) &&
2108 LineMatch (L2[4], "\tldy\t#$") &&
2109 GetHexNum (L2[4]->Line+7) == Offs &&
2110 LineFullMatch (L2[5], "\tjsr\tstaxysp") &&
2111 LineFullMatch (L2[6], "\tlda\tregsave") &&
2112 LineFullMatch (L2[7], "\tldx\tregsave+1")) {
2121 } else if (GetNextCodeLines (L, L2, 13) &&
2122 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
2123 LineFullMatch (L2[1], "\ttax") &&
2124 LineFullMatch (L2[2], "\tdey") &&
2125 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
2126 LineFullMatch (L2[4], "\tsta\tregsave") &&
2127 LineFullMatch (L2[5], "\tstx\tregsave+1") &&
2128 LineFullMatch (L2[6], "\tclc") &&
2129 LineMatch (L2[7], "\tadc\t#$") &&
2130 LineFullMatch (L2[8], "\tbcc\t*+3") &&
2131 LineFullMatch (L2[9], "\tinx")) {
2133 /* Inlined version */
2134 Inc = GetHexNum (L2[7]->Line+7);
2136 /* Check for stack offset zero */
2137 if (LineFullMatch (L2[10], "\tjsr\tstax0sp") &&
2138 LineFullMatch (L2[11], "\tlda\tregsave") &&
2139 LineFullMatch (L2[12], "\tldx\tregsave+1")) {
2143 } else if (GetNextCodeLines (L2[12], &L2[13], 1) &&
2144 LineMatch (L2[10], "\tldy\t#$") &&
2145 GetHexNum (L2[10]->Line+7) == Offs &&
2146 LineFullMatch (L2[11], "\tjsr\tstaxysp") &&
2147 LineFullMatch (L2[12], "\tlda\tregsave") &&
2148 LineFullMatch (L2[13], "\tldx\tregsave+1")) {
2161 /* Get a pointer to the last line of the preceding sequence */
2162 L3 = &L2[LinesToRemove-1];
2164 /* Check if AX is actually used following the code above. If not,
2165 * we don't need to load A/X from regsave. Since X will never by
2166 * used without A, check just for A.
2168 NeedLoad = RegAUsed (L3[0]);
2170 /* Replace the ldy instruction, offset must point to the low byte */
2171 sprintf (L->Line+7, "%02X", Offs);
2173 /* Insert the first part of the code */
2174 L = NewLineAfter (L, "\tlda\t(sp),y");
2176 L = NewLineAfter (L, "\tsta\tptr1");
2178 L = NewLineAfter (L, "\tclc");
2179 L = NewLineAfter (L, "\tadc\t#$%02X", Inc);
2180 L = NewLineAfter (L, "\tsta\t(sp),y");
2181 L = NewLineAfter (L, "\tiny");
2182 L = NewLineAfter (L, "\tlda\t(sp),y");
2184 L = NewLineAfter (L, "\tsta\tptr1+1");
2186 L = NewLineAfter (L, "\tadc\t#$00");
2187 L = NewLineAfter (L, "\tsta\t(sp),y");
2189 /* Check if we must really load the old value into a/x or if the
2190 * code may be replaced by something else.
2192 if (GetNextCodeLines (L3[0], &L3[1], 1)) {
2193 if (LineFullMatch (L3[1], "\tjsr\tldaui")) {
2194 /* Load char indirect */
2195 L = NewLineAfter (L, "\tldx\t#$00");
2196 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2199 } else if (LineFullMatch (L3[1], "\tsta\tptr1") &&
2200 GetNextCodeLines (L3[1], &L3[2], 3) &&
2201 LineFullMatch (L3[2], "\tstx\tptr1+1") &&
2202 LineFullMatch (L3[3], "\tldx\t#$00") &&
2203 LineFullMatch (L3[4], "\tlda\t(ptr1,x)")) {
2205 /* Load char indirect, inlined */
2206 L = NewLineAfter (L, "\tldx\t#$00");
2207 L = NewLineAfter (L, "\tlda\t(ptr1,x)");
2211 } else if (LineFullMatch (L3[1], "\tjsr\tldaxi")) {
2212 /* Load word indirect */
2213 L = NewLineAfter (L, "\tldy\t#$01");
2214 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2215 L = NewLineAfter (L, "\ttax");
2216 L = NewLineAfter (L, "\tdey");
2217 L = NewLineAfter (L, "\tlda\t(ptr1),y");
2221 } else if (LineFullMatch (L3[1], "\tjsr\tpushax")) {
2222 if (GetNextCodeLines (L3[1], &L3[2], 2) &&
2223 LineMatch (L3[2], "\tlda\t") &&
2224 LineFullMatch (L3[3], "\tjsr\tstaspp")) {
2226 /* Store to pointer */
2227 L = NewLineAfter (L, L3[2]->Line);
2228 L = NewLineAfter (L, "\tldy\t#$00");
2229 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2233 } else if (GetNextCodeLines (L3[1], &L3[2], 3) &&
2234 LineMatch (L3[2], "\tldy\t#$") &&
2235 LineFullMatch (L3[3], "\tlda\t(sp),y") &&
2236 LineFullMatch (L3[4], "\tjsr\tstaspp")) {
2238 /* Beware: We have to correct the stack offset, since we will
2239 * remove the pushax instruction!
2241 sprintf (L3[2]->Line+7, "%02X", GetHexNum (L3[2]->Line+7)-2);
2243 /* Store to pointer */
2244 L = NewLineAfter (L, L3[2]->Line);
2245 L = NewLineAfter (L, L3[3]->Line);
2246 L = NewLineAfter (L, "\tldy\t#$00");
2247 L = NewLineAfter (L, "\tsta\t(ptr1),y");
2256 /* If we need to load a/x, add the code */
2258 L = NewLineAfter (L, "\tlda\tptr1");
2259 L = NewLineAfter (L, "\tldx\tptr1+1");
2262 /* Remove the code that is no longer needed */
2263 FreeLines (L2[0], L2[LinesToRemove-1]);
2265 /* Return the new line and success */
2266 *Start = NextCodeLine (L);
2272 static void OptPtrOps (void)
2273 /* Optimize several pointer and array constructs */
2277 Line* L = FirstCode;
2280 if (OptPtrOps1 (&L)) {
2282 } else if (OptPtrOps2 (&L)) {
2286 /* Search for the following sequence:
2294 * and replace it by:
2301 else if (LineFullMatch (L, "\tlda\tregsave") && /* Match on start */
2302 GetNextCodeLines (L, L2, 4) && /* Fetch next lines */
2303 LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */
2304 LineFullMatch (L2 [1], "\tjsr\tpushax") &&
2305 LineMatch (L2 [2], "\tlda\t#$") &&
2306 LineFullMatch (L2 [3], "\tjsr\tstaspp")) {
2308 /* Found the sequence, replace it */
2309 L = ReplaceLine (L, L2 [2]->Line); /* lda #$.. */
2310 L2 [0] = ReplaceLine (L2 [0], "\tldy\t#$00");
2311 L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
2313 /* Free the remaining lines */
2314 FreeLines (L2 [2], L2 [3]);
2317 /* Search for the following sequence:
2323 * and replace it by:
2329 else if (LineFullMatch (L, "\tlda\tregsave") && /* Match on start */
2330 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
2331 LineFullMatch (L2 [0], "\tldx\tregsave+1") && /* Match line 2 ... */
2332 LineFullMatch (L2 [1], "\tjsr\tldaui")) {
2334 /* Found the sequence, replace it */
2335 L = ReplaceLine (L, "\tldx\t#$00");
2336 L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
2338 /* Free the remaining lines */
2343 * Search for the following sequence:
2352 * and replace it by:
2363 else if (LineFullMatch (L, "\tlda\tregsave") &&
2364 GetNextCodeLines (L, L2, 5) &&
2365 LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
2366 LineFullMatch (L2 [1], "\tjsr\tpushax") &&
2367 LineMatch (L2 [2], "\tldx\t#$") &&
2368 LineMatch (L2 [3], "\tlda\t#$") &&
2369 LineFullMatch (L2 [4], "\tjsr\tstaxspp")) {
2371 /* Found the sequence, replace it */
2372 L = ReplaceLine (L, "\tldy\t#$01");
2373 L2 [0] = ReplaceLine (L2 [0], L2 [2]->Line);
2374 L2 [0]->Line [3] = 'a';
2375 L2 [1] = ReplaceLine (L2 [1], "\tsta\t(regsave),y");
2376 L2 [4] = ReplaceLine (L2 [4], L2 [3]->Line);
2377 L2 [2] = ReplaceLine (L2 [2], "\ttax");
2378 L2 [3] = ReplaceLine (L2 [3], "\tdey");
2379 L = NewLineAfter (L2 [4], "\tsta\t(regsave),y");
2383 * Search for the following sequence:
2392 * and replace it by:
2398 else if (LineFullMatch (L, "\tlda\tregsave") &&
2399 GetNextCodeLines (L, L2, 5) &&
2400 LineFullMatch (L2 [0], "\tldx\tregsave+1") &&
2401 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2402 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2403 LineFullMatch (L2 [3], "\tldx\t#$00") &&
2404 LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
2406 /* Found the sequence, replace it */
2407 L = ReplaceLine (L, "\tldx\t#$00");
2408 L2 [0] = ReplaceLine (L2 [0], "\tlda\t(regsave,x)");
2410 /* Remove the remaining lines */
2411 FreeLines (L2 [1], L2 [4]);
2414 /* Search for the following sequence:
2420 * and replace it by:
2429 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2430 GetNextCodeLines (L, L2, 2) &&
2431 LineMatch (L2 [0], "\tlda\t") &&
2432 LineFullMatch (L2 [1], "\tjsr\tstaspp")) {
2434 /* Found the sequence, replace it */
2435 L = ReplaceLine (L, "\tsta\tptr1");
2436 L2 [1] = ReplaceLine (L2 [1], L2 [0]->Line); /* lda ... */
2437 L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
2438 L2 [2] = NewLineAfter (L2 [1], "\tldy\t#$00");
2439 L = NewLineAfter (L2 [2], "\tsta\t(ptr1),y");
2442 /* Search for the following sequence:
2449 * and replace it by:
2458 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2459 GetNextCodeLines (L, L2, 3) &&
2460 LineMatch (L2 [0], "\tlda\t") &&
2461 LineMatch (L2 [1], "\tldy\t#$") &&
2462 LineFullMatch (L2 [2], "\tjsr\tstaspidx")) {
2464 /* Found the sequence, replace it */
2465 L = ReplaceLine (L, "\tsta\tptr1");
2466 L = NewLineAfter (L, "\tstx\tptr1+1");
2467 L2 [2] = ReplaceLine (L2 [2], "\tsta\t(ptr1),y");
2470 /* Search for the following sequence:
2477 * and replace it by:
2486 * Beware: Since we remove a call to a function that changes the stack
2487 * pointer, we have to adjust the stack address for the lda.
2490 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2491 GetNextCodeLines (L, L2, 3) &&
2492 LineMatch (L2 [0], "\tldy\t#$") &&
2493 LineFullMatch (L2 [1], "\tlda\t(sp),y") &&
2494 LineFullMatch (L2 [2], "\tjsr\tstaspp")) {
2496 /* Found the sequence, replace it. First create a new load
2497 * instruction for the changed stack offset.
2500 sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
2501 L = ReplaceLine (L, "\tsta\tptr1");
2502 L2 [1] = ReplaceLine (L2 [1], Buf); /* ldy ... */
2503 L2 [0] = ReplaceLine (L2 [0], "\tstx\tptr1+1");
2504 L2 [2] = ReplaceLine (L2 [2], "\tlda\t(sp),y");
2505 L2 [3] = NewLineAfter (L2 [2], "\tldy\t#$00");
2506 L = NewLineAfter (L2 [3], "\tsta\t(ptr1),y");
2509 /* Search for the following sequence:
2517 * and replace it by:
2526 * Beware: Since we remove a call to a function that changes the stack
2527 * pointer, we have to adjust the stack address for the lda.
2530 else if (LineFullMatch (L, "\tjsr\tpushax") &&
2531 GetNextCodeLines (L, L2, 4) &&
2532 LineMatch (L2 [0], "\tldy\t#$") &&
2533 LineFullMatch (L2 [1], "\tlda\t(sp),y") &&
2534 LineMatch (L2 [2], "\tldy\t#$") &&
2535 LineFullMatch (L2 [3], "\tjsr\tstaspidx")) {
2537 /* Found the sequence, replace it. First create a new load
2538 * instruction for the changed stack offset.
2541 sprintf (Buf, "\tldy\t#$%02X", GetHexNum (L2 [0]->Line+7) - 2);
2542 L = ReplaceLine (L, "\tsta\tptr1");
2543 L = NewLineAfter (L, "\tstx\tptr1+1");
2544 L2 [0] = ReplaceLine (L2 [0], Buf); /* ldy ... */
2545 L2 [3] = ReplaceLine (L2 [3], "\tsta\t(ptr1),y");
2548 /* Search for the following sequence:
2561 * and replace it by:
2569 * The load of X may be omitted if X is not used below.
2571 else if (LineMatch (L, "\tldax\t_") &&
2572 GetNextCodeLines (L, L2, 9) &&
2573 LineMatch (L2 [0], "\tldy\t#$") &&
2574 LineFullMatch (L2 [1], "\tclc") &&
2575 LineFullMatch (L2 [2], "\tadc\t(sp),y") &&
2576 LineFullMatch (L2 [3], "\tbcc\t*+3") &&
2577 LineFullMatch (L2 [4], "\tinx") &&
2578 LineFullMatch (L2 [5], "\tsta\tptr1") &&
2579 LineFullMatch (L2 [6], "\tstx\tptr1+1") &&
2580 LineFullMatch (L2 [7], "\tldx\t#$00") &&
2581 LineFullMatch (L2 [8], "\tlda\t(ptr1,x)")) {
2583 /* Found the sequence, replace it */
2585 strcpy (Label, L->Line + 6); /* Remember the label */
2586 L = ReplaceLine (L, L2 [0]->Line); /* ldy .. */
2587 L = NewLineAfter (L, "\tlda\t(sp),y");
2588 L = NewLineAfter (L, "\ttay");
2589 if (RegXUsed (L2[8])) {
2590 L = NewLineAfter (L, "\tldx\t#$00");
2592 L = NewLineAfter (L, "\tlda\t%s,y", Label);
2594 /* Remove the remaining stuff. There may be hints between the
2595 * instructions, remove them too
2597 FreeLines (L2[0], L2 [8]);
2621 * This change will cost 2 cycles, but it saves a lot of code (6 bytes
2622 * per occurrence), so we will accept the overhead. It may even be
2623 * possible to rewrite the library routine to get rid of the additional
2626 else if (LineMatch (L, "\tldy\t#$") &&
2627 GetNextCodeLines (L, L2, 6) &&
2628 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
2629 LineFullMatch (L2 [1], "\ttax") &&
2630 LineFullMatch (L2 [2], "\tdey") &&
2631 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
2632 LineMatch (L2 [4], "\tldy\t#$") &&
2633 LineFullMatch (L2 [5], "\tjsr\tldauidx")) {
2635 /* Found - replace it */
2636 L2 [4]->Line [3] = 'x'; /* Change to ldx */
2637 if (LineFullMatch (L, "\tldy\t#$01")) {
2638 /* Word at offset zero */
2640 L = ReplaceLine (L2 [5], "\tjsr\tldaui0sp");
2642 ReplaceLine (L2 [5], "\tjsr\tldauiysp");
2645 /* Delete the remaining lines */
2646 FreeLines (L2 [0], L2 [3]);
2670 * This change will has an overhead of 10 cycles, but it saves 11(!)
2671 * bytes per invocation. Maybe we should apply only if FavourSize is
2674 else if (LineMatch (L, "\tldy\t#$") &&
2675 GetNextCodeLines (L, L2, 8) &&
2676 LineFullMatch (L2 [0], "\tlda\t(sp),y") &&
2677 LineFullMatch (L2 [1], "\ttax") &&
2678 LineFullMatch (L2 [2], "\tdey") &&
2679 LineFullMatch (L2 [3], "\tlda\t(sp),y") &&
2680 LineFullMatch (L2 [4], "\tsta\tptr1") &&
2681 LineFullMatch (L2 [5], "\tstx\tptr1+1") &&
2682 LineFullMatch (L2 [6], "\tldx\t#$00") &&
2683 LineFullMatch (L2 [7], "\tlda\t(ptr1,x)")) {
2685 /* Found - replace it */
2686 if (LineFullMatch (L, "\tldy\t#$01")) {
2687 /* Word at offset zero */
2689 L = ReplaceLine (L2 [0], "\tjsr\tldau00sp");
2691 ReplaceLine (L2 [0], "\tjsr\tldau0ysp");
2694 /* Delete the remaining lines */
2695 FreeLines (L2 [1], L2 [7]);
2699 L = NextCodeLine (L);
2705 static void OptRegVars (void)
2706 /* Optimize register variable uses */
2710 Line* L = FirstCode;
2713 /* Search for the following sequence:
2719 * and replace it by:
2725 if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2726 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
2727 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2728 LineFullMatch (L2 [1], "\tjsr\tldaui") &&
2729 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2730 strcmp (L2 [0]->Line + 14, "+1") == 0) {
2733 sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
2735 /* Found the sequence, replace it */
2736 L = ReplaceLine (L, "\tldx\t#$00");
2737 L2 [0] = ReplaceLine (L2 [0], Buf);
2739 /* Free the remaining lines */
2743 /* Search for the following sequence:
2752 * and replace it by:
2758 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2759 GetNextCodeLines (L, L2, 5) && /* Fetch next lines */
2760 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2761 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2762 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2763 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2764 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2765 LineFullMatch (L2 [3], "\tldx\t#$00") &&
2766 LineFullMatch (L2 [4], "\tlda\t(ptr1,x)")) {
2769 sprintf (Buf, "\tlda\t(%s,x)", L->Line + 5);
2771 /* Found the sequence, replace it */
2772 L = ReplaceLine (L, "\tldx\t#$00");
2773 L2 [0] = ReplaceLine (L2 [0], Buf);
2775 /* Free the remaining lines */
2776 FreeLines (L2 [1], L2 [4]);
2779 /* Search for the following sequence:
2786 * and replace it by:
2793 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2794 GetNextCodeLines (L, L2, 3) && /* Fetch next lines */
2795 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2796 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2797 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2798 LineMatch (L2 [1], "\tldy\t#$") &&
2799 LineFullMatch (L2 [2], "\tjsr\tldauidx")) {
2802 sprintf (Buf, "\tlda\t(%s),y", L->Line + 5);
2804 /* Found the sequence, replace it */
2805 L = ReplaceLine (L, L2 [1]->Line);
2806 L2 [0] = ReplaceLine (L2 [0], "\tldx\t#$00");
2807 L2 [1] = ReplaceLine (L2 [1], Buf);
2809 /* Free the remaining lines */
2813 /* Search for the following sequence:
2823 * and replace it by:
2829 * The source form is not generated by the parser but by the optimizer.
2831 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2832 GetNextCodeLines (L, L2, 6) && /* Fetch next lines */
2833 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2834 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2835 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2836 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2837 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2838 LineMatch (L2 [3], "\tlda\t") &&
2839 LineMatch (L2 [4], "\tldy\t#$") &&
2840 LineMatch (L2 [5], "\tsta\t(ptr1),y")) {
2843 sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
2845 /* Found the sequence, replace it */
2846 L2 [5] = ReplaceLine (L2 [5], Buf);
2848 /* Free the remaining lines */
2849 FreeLines (L, L2 [2]);
2851 /* Make the line pointer valid again */
2855 /* Search for the following sequence:
2866 * and replace it by:
2873 * The source form is not generated by the parser but by the optimizer.
2875 else if (LineMatch (L, "\tlda\tregbank+") && /* Match on start */
2876 GetNextCodeLines (L, L2, 7) && /* Fetch next lines */
2877 LineMatch (L2 [0], "\tldx\tregbank+") && /* Match line 2 ... */
2878 L->Line [13] == L2 [0]->Line [13] && /* Offset equal */
2879 strcmp (L2 [0]->Line + 14, "+1") == 0 &&
2880 LineFullMatch (L2 [1], "\tsta\tptr1") &&
2881 LineFullMatch (L2 [2], "\tstx\tptr1+1") &&
2882 LineMatch (L2 [3], "\tldy\t#$") &&
2883 LineFullMatch (L2 [4], "\tlda\t(sp),y") &&
2884 LineMatch (L2 [5], "\tldy\t#$") &&
2885 LineMatch (L2 [6], "\tsta\t(ptr1),y")) {
2888 sprintf (Buf, "\tsta\t(%s),y", L->Line + 5);
2890 /* Found the sequence, replace it */
2891 L2 [6] = ReplaceLine (L2 [6], Buf);
2893 /* Free the remaining lines */
2894 FreeLines (L, L2 [2]);
2896 /* Make the line pointer valid again */
2901 L = NextCodeLine (L);
2907 static void OptDoubleJumps (void)
2908 /* Remove/rearrange jumps that jump to other jumps */
2910 static const char* Jumps [] = {
2921 Line* L = FirstCode;
2926 /* Is this a jump? */
2927 while ((I = LineMatchX (L, Jumps)) >= 0) {
2929 /* Yes. Get the target label */
2930 Line* Target = GetTargetLine (L->Line + 5);
2932 /* Target points to the label itself. Skip lines until we reach
2933 * one that is not a label.
2935 Target = NextInstruction (Target);
2937 /* Be sure, this line is not the same as the one the jump is
2938 * in (this happens if there is an empty loop).
2944 if (LineMatch (Target, "\tjmp\t")) {
2946 /* The target is itself a jump. If this is a short branch, get
2947 * the final target and check if it is in reach. Bail out if
2950 if (L->Line[1] == 'b') {
2951 Line* FinalTarget = GetTargetLine (Target->Line+5);
2952 FinalTarget = NextInstruction (FinalTarget);
2953 if ((D = GetJumpDistance (L, FinalTarget)) >= 123) {
2958 /* Make sure the jump does indeed point to another label.
2959 * It may happen that this is not the case for some endless
2960 * loop (while(1) and similar).
2962 if (strcmp (L->Line+5, Target->Line+5) == 0) {
2963 /* Same label, bail out */
2967 /* Use the label in the original jump instead */
2968 L = ReplaceLine (L, "%.5s%s", L->Line, Target->Line+5);
2970 } else if (I < 2 && LineMatch (Target, Jumps [I])) {
2972 /* Conditional jump. Use final label */
2973 strcpy (L->Line+5, Target->Line+5);
2981 L = NextCodeLine (L);
2987 static void OptJumpRTS (void)
2988 /* Replace jumps to an RTS by an RTS */
2990 Line* L = FirstCode;
2992 /* Is this a jump to a numbered label? */
2993 if (LineMatch (L, "\tjmp\t") && L->Line [5] == 'L' && isdigit (L->Line [6])) {
2995 /* Yes. Get the target label */
2996 Line* Target = GetTargetLine (L->Line+5);
2998 /* Target points to the label itself. Get the next line */
2999 Target = NextCodeLine (Target);
3000 if (LineFullMatch (Target, "\trts")) {
3001 /* Replace the jump by an RTS */
3002 L = ReplaceLine (L, "\trts");
3005 L = NextCodeLine (L);
3011 static void OptBoolTransforms (void)
3012 /* Try to remove the boolean transformation subroutines where they aren't
3018 const char* BranchTarget;
3020 Line* L = FirstCode;
3023 /* Search for a boolean transformer followed by a conditional jump. */
3024 if (LineMatch (L, "\tjsr\tbool") &&
3025 GetNextCodeLines (L, L2, 1) &&
3026 IsCondJump (L2 [0])) {
3028 /* Make the boolean transformer unnecessary by changing the
3029 * the conditional jump to evaluate the condition flags that
3030 * are set after the compare directly. Note: jeq jumps if
3031 * the condition is not met, jne jumps if the condition is met.
3034 /* Get the condition code */
3035 int Cond = FindCond (L->Line + 9);
3041 /* Invert the code if we jump on condition not met. */
3042 if (L2[0]->Line [2] == 'e' && L2[0]->Line [3] == 'q') {
3043 /* Jumps if condition false, invert condition */
3044 Cond = CmpInvertTab [Cond];
3047 /* For easier reading, get a pointer to the jump target */
3048 BranchTarget = L2[0]->Line+5;
3050 /* Check if we can replace the jump (sometimes we would need two
3051 * conditional jumps, we will not handle that for now since it
3052 * has some complications - both jumps may be far jumps for
3053 * example making the jumps more costly than the bool transformer
3054 * subroutine). If we cannot replace the jump, bail out.
3059 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3063 L = ReplaceLine (L, "\tjne\t%s", BranchTarget);
3067 Label = AllocLabel ();
3068 L = ReplaceLine (L, "\tbeq\tL%04X", Label);
3069 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3070 L = NewLabelAfter(L, Label);
3074 L = ReplaceLine (L, "\tjpl\t%s", BranchTarget);
3078 L = ReplaceLine (L, "\tjmi\t%s", BranchTarget);
3082 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3083 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3087 Label = AllocLabel ();
3088 L = ReplaceLine (L, "\tbeq\tL%04X", Label);
3089 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3090 L = NewLabelAfter(L, Label);
3094 L = ReplaceLine (L, "\tjcs\t%s", BranchTarget);
3098 L = ReplaceLine (L, "\tjcc\t%s", BranchTarget);
3102 L = ReplaceLine (L, "\tjeq\t%s", BranchTarget);
3103 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3107 Internal ("Unknown jump condition: %u", Cond);
3111 /* Remove the old stuff */
3117 L = NextCodeLine (L);
3123 static void OptCompares2 (void)
3124 /* Try to optimize the integer compare subroutines. */
3128 const char* BranchTarget;
3131 Line* L = FirstCode;
3149 if (LineMatch (L, "\tlda\t") &&
3150 GetNextCodeLines (L, L2, 5) &&
3151 IsLoadAX (L, L2[0]) &&
3152 LineFullMatch (L2[1], "\tcpx\t#$00") &&
3153 LineFullMatch (L2[2], "\tbne\t*+4") &&
3154 LineFullMatch (L2[3], "\tcmp\t#$00") &&
3155 IsCondJump (L2[4])) {
3157 /* Replace the load of X by an ora */
3158 L2[0]->Line[1] = 'o';
3159 L2[0]->Line[2] = 'r';
3160 L2[0]->Line[3] = 'a';
3162 /* Remove unneeded stuff */
3163 FreeLines (L2[1], L2[3]);
3167 /* Same for local variables: Replace
3187 else if (LineMatch (L, "\tldy\t#$") &&
3188 GetNextCodeLines (L, L2, 8) &&
3189 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
3190 LineFullMatch (L2[1], "\ttax") &&
3191 LineFullMatch (L2[2], "\tdey") &&
3192 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
3193 LineFullMatch (L2[4], "\tcpx\t#$00") &&
3194 LineFullMatch (L2[5], "\tbne\t*+4") &&
3195 LineFullMatch (L2[6], "\tcmp\t#$00") &&
3196 IsCondJump (L2[7])) {
3198 /* Replace the second load by an ora */
3199 L2[3]->Line[1] = 'o';
3200 L2[3]->Line[2] = 'r';
3201 L2[3]->Line[3] = 'a';
3203 /* Remove unneeded stuff */
3205 FreeLines (L2[4], L2[6]);
3209 /* Search for the call to a compare subroutine followed by a
3212 else if (LineMatch (L, "\tjsr\ttos") &&
3213 (L2[0] = NextCodeLine (L)) != 0 &&
3214 IsCondJump (L2[0])) {
3216 /* Extract the condition from the function name and branch */
3217 C = CheckAndGetIntCmp (L, L2[0]);
3219 /* Something is wrong */
3223 /* Replace the subroutine call by a cheaper one */
3224 L = ReplaceLine (L, "\tjsr\ttosicmp");
3226 /* For easier reading, get a pointer to the jump target */
3227 BranchTarget = L2[0]->Line+5;
3229 /* Check if we can replace the jump (sometimes we would need two
3230 * conditional jumps, we will not handle that for now since it
3231 * has some complications - both jumps may be far jumps for
3232 * example making the jumps more costly than the bool transformer
3233 * subroutine). If we cannot replace the jump, bail out.
3238 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3242 L = NewLineAfter (L, "\tjne\t%s", BranchTarget);
3246 Label = AllocLabel ();
3247 L = NewLineAfter (L, "\tbeq\tL%04X", Label);
3248 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3249 L = NewLabelAfter(L, Label);
3253 L = NewLineAfter (L, "\tjpl\t%s", BranchTarget);
3257 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3261 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3262 L = NewLineAfter (L, "\tjmi\t%s", BranchTarget);
3266 Label = AllocLabel ();
3267 L = NewLineAfter (L, "\tbeq\tL%04X", Label);
3268 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3269 L = NewLabelAfter(L, Label);
3273 L = NewLineAfter (L, "\tjcs\t%s", BranchTarget);
3277 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3281 L = NewLineAfter (L, "\tjeq\t%s", BranchTarget);
3282 L = NewLineAfter (L, "\tjcc\t%s", BranchTarget);
3286 Internal ("Unknown jump condition: %u", C);
3290 /* Remove the old stuff */
3295 L = NextCodeLine (L);
3301 static void OptTests (void)
3302 /* Remove unnecessary tests */
3306 const char* BitOps [] = {
3313 /* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
3320 Line* L = FirstCode;
3323 /* Search for lda/tay/jne or lda/tay/jeq, remove the tay.
3330 if ((LineMatch (L, "\tlda\t") ||
3331 LineMatch (L, "\tand\t") ||
3332 LineMatch (L, "\tora\t") ||
3333 LineMatch (L, "\teor\t")) &&
3334 GetNextCodeLines (L, L2, 2) &&
3335 (LineFullMatch (L2 [0], "\ttay") ||
3336 LineFullMatch (L2 [0], "\tcmp\t#$00")) &&
3337 IsCondJump (L2 [1])) {
3339 /* We can remove the tay */
3350 * and remove the tax.
3352 else if (LineMatchX (L, BitOps) >= 0 &&
3353 GetNextCodeLines (L, L2, 2) &&
3354 LineFullMatch (L2[0], "\ttax") &&
3355 IsCondJump (L2[1])) {
3357 /* Remove the tax including a hint line of there is one */
3358 if (LineFullMatch (L2[0]->Prev, "+forcetest")) {
3359 FreeLine (L2[0]->Prev);
3363 /* If the line before L loads X, this is useless and may be removed */
3364 L2[0] = PrevCodeLine (L);
3365 if (LineFullMatch (L2[0], "\tldx\t#$00")) {
3371 /* Search for the sequence
3382 else if (LineMatch (L, "\tstx\t") &&
3383 GetNextCodeLines (L, L2, 2) &&
3384 LineFullMatch (L2[0], "\tstx\ttmp1") &&
3385 LineFullMatch (L2[1], "\tora\ttmp1")) {
3387 /* Found, replace it */
3388 L = NewLineAfter (L, "\tora\t%s", L->Line+5);
3390 /* Remove remaining stuff */
3391 FreeLines (L2[0], L2[1]);
3397 L = NextCodeLine (L);
3403 static void OptNeg (void)
3404 /* Optimize the "bnegax/jeq" and "bnegax/jne" sequences */
3408 Line* L = FirstCode;
3411 /* Search for the sequence:
3417 * and replace it by:
3422 if (LineMatch (L, "\tlda\t") && /* Match on start */
3423 GetNextCodeLines (L, L2, 2) && /* Fetch next lines */
3424 LineFullMatch (L2 [0], "\tjsr\tbnega") &&
3425 IsCondJump (L2 [1])) {
3427 /* Found the sequence, replace it */
3429 InvertZJump (L2 [1]);
3433 /* Search for the sequence:
3451 else if (LineMatch (L, "\tldy\t#$") &&
3452 GetNextCodeLines (L, L2, 6) &&
3453 LineFullMatch (L2[0], "\tlda\t(sp),y") &&
3454 LineFullMatch (L2[1], "\ttax") &&
3455 LineFullMatch (L2[2], "\tdey") &&
3456 LineFullMatch (L2[3], "\tlda\t(sp),y") &&
3457 LineFullMatch (L2[4], "\tjsr\tbnegax") &&
3458 IsCondJump (L2[5])) {
3460 L2[1] = ReplaceLine (L2[1], "\tdey");
3461 L2[2] = ReplaceLine (L2[2], "\tora\t(sp),y");
3462 FreeLines (L2[3], L2[4]);
3463 InvertZJump (L2[5]);
3467 /* Search for the sequence:
3480 else if (LineMatch (L, "\tlda\t") &&
3481 GetNextCodeLines (L, L2, 3) &&
3482 IsLoadAX (L, L2[0]) &&
3483 LineFullMatch (L2[1], "\tjsr\tbnegax") &&
3484 IsCondJump (L2[2])) {
3486 /* Replace the load of X by ora */
3487 L2[0]->Line[1] = 'o';
3488 L2[0]->Line[2] = 'r';
3489 L2[0]->Line[3] = 'a';
3491 InvertZJump (L2[2]);
3495 /* Search for the sequence:
3501 * and replace it by:
3507 else if (LineMatch (L, "\tjsr\t_") && /* Match on start */
3508 GetNextCodeLines (L, L2, 2) &&
3509 LineMatch (L2 [0], "\tjsr\tbnega") &&
3510 IsCondJump (L2 [1])) {
3512 if (LineFullMatch (L2 [0], "\tjsr\tbnega")) {
3514 L2 [0] = ReplaceLine (L2 [0], "\ttax"); /* Test a */
3517 L2 [0] = ReplaceLine (L2 [0], "\tstx\ttmp1");
3518 NewLineAfter (L2 [0], "\tora\ttmp1");
3521 /* Invert the jump */
3522 InvertZJump (L2 [1]);
3527 L = NextCodeLine (L);
3533 static void OptTriples (void)
3534 /* Replace code triples */
3536 static const char* Pat1 [] = {
3544 static const char* Pat2 [] = {
3552 static const char* Replace [] = {
3560 Line* L = FirstCode;
3562 int I = LineFullMatchX (L, Pat1);
3564 /* We found the first match, get the next line */
3565 Line* L2 = NextCodeLine (L);
3566 if (L2 && LineFullMatch (L2, Pat2 [I])) {
3567 /* Found. Replace by the short call */
3569 L = ReplaceLine (L, Replace [I]);
3573 L = NextCodeLine (L);
3579 static Line* OptOneBlock (Line* L)
3580 /* Optimize the register contents inside one basic block */
3582 static const char* Compares [] = {
3583 "\tjsr\ttoseq00", "\tjsr\ttoseqa0", "\tjsr\ttoseqax",
3584 "\tjsr\ttoseqeax", "\tjsr\ttosne00", "\tjsr\ttosnea0",
3585 "\tjsr\ttosneax", "\tjsr\ttosneeax", "\tjsr\ttoslt00",
3586 "\tjsr\ttoslta0", "\tjsr\ttosltax", "\tjsr\ttosult00",
3587 "\tjsr\ttosulta0", "\tjsr\ttosultax", "\tjsr\ttoslteax",
3588 "\tjsr\ttosulteax", "\tjsr\ttosle00", "\tjsr\ttoslea0",
3589 "\tjsr\ttosleax", "\tjsr\ttosule00", "\tjsr\ttosulea0",
3590 "\tjsr\ttosuleax", "\tjsr\ttosleeax", "\tjsr\ttosuleeax",
3591 "\tjsr\ttosgt00", "\tjsr\ttosgta0", "\tjsr\ttosgtax",
3592 "\tjsr\ttosugt00", "\tjsr\ttosugta0", "\tjsr\ttosugtax",
3593 "\tjsr\ttosgteax", "\tjsr\ttosugteax", "\tjsr\ttosge00",
3594 "\tjsr\ttosgea0", "\tjsr\ttosgeax", "\tjsr\ttosuge00",
3595 "\tjsr\ttosugea0", "\tjsr\ttosugeax", "\tjsr\ttosgeeax",
3600 static const char* MakeBool [] = {
3601 "\tjsr\tbooleq", "\tjsr\tboolne", "\tjsr\tboollt",
3602 "\tjsr\tboolle", "\tjsr\tboolgt", "\tjsr\tboolge",
3603 "\tjsr\tboolult", "\tjsr\tboolule", "\tjsr\tboolugt",
3608 int A = -1; /* Contents of A register */
3609 int X = -1; /* Contents of X register */
3610 int Y = -1; /* Contents of Y register */
3615 while (L && !IsLabel (L)) {
3617 /* Handle all instructions. All instructions not tested here have
3618 * no effects on the register contents.
3621 if (L->Line [0] == '+') {
3622 /* This is a hint */
3623 if (LineMatch (L, "+a:")) {
3624 /* Information about a */
3625 switch (L->Line [3]) {
3626 case '!': A = -1; break;
3627 case '=': A = GetHexNum (L->Line + 4); break;
3629 } else if (LineMatch (L, "+x:")) {
3630 /* The code generator tells something about the x register */
3631 switch (L->Line [3]) {
3632 case '!': X = -1; break;
3633 case '=': X = GetHexNum (L->Line + 4); break;
3635 } else if (LineMatch (L, "+y:")) {
3636 /* Information about the y register */
3637 switch (L->Line [3]) {
3638 case '!': Y = -1; break;
3639 case '=': Y = GetHexNum (L->Line + 4); break;
3642 } else if (LineMatch (L, "\tadc\t")) {
3643 if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
3644 L->Line[strlen(L->Line)-2] = '\0';
3647 } else if (LineMatch (L, "\tand\t")) {
3649 } else if (LineFullMatch (L, "\tasl\ta")) {
3651 A = (A << 1) & 0xFF;
3653 } else if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
3654 L->Line[strlen(L->Line)-2] = '\0';
3655 } else if (CPU == CPU_65C02 && (LineFullMatch (L, "\tdea") ||
3656 LineFullMatch (L, "\tdec\ta"))) {
3658 } else if (LineFullMatch (L, "\tdex")) {
3660 } else if (LineFullMatch (L, "\tdey")) {
3662 } else if (LineMatch (L, "\teor")) {
3664 } else if (CPU == CPU_65C02 && (LineFullMatch (L, "\tina") ||
3665 LineFullMatch (L, "\tinc\ta"))) {
3667 } else if (LineFullMatch (L, "\tinx")) {
3669 } else if (LineFullMatch (L, "\tiny")) {
3671 } else if (LineFullMatch (L, "\tjsr\taddeq0sp")) {
3672 /* We know about this function */
3675 } else if (LineFullMatch (L, "\tjsr\taddeqysp")) {
3676 /* We know about this function */
3679 } else if (LineFullMatch (L, "\tjsr\taxulong")) {
3680 /* We know about this function and we're trying to replace it by
3681 * inline code if we have already a register that contains zero.
3686 } else if (X == 0) {
3688 } else if (Y == 0) {
3694 /* We cannot replace the code, but we know about the results */
3697 L = ReplaceLine (L, "\tst%c\tsreg", C);
3698 NewLineAfter (L, "\tst%c\tsreg+1", C);
3700 } else if (LineFullMatch (L, "\tjsr\tbnega")) {
3701 /* We know about this function */
3704 } else if (LineFullMatch (L, "\tjsr\tbnegax")) {
3705 /* We know about this function */
3708 } else if (LineFullMatch (L, "\tjsr\tbnegeax")) {
3709 /* We know about this function */
3712 } else if (LineFullMatch (L, "\tjsr\tcomplax")) {
3713 /* We know about this function */
3720 } else if (LineFullMatch (L, "\tjsr\tdecax1")) {
3721 /* We know about this function */
3723 } else if (LineFullMatch (L, "\tjsr\tdecax2")) {
3724 /* We know about this function */
3726 } else if (LineFullMatch (L, "\tjsr\tdecaxy")) {
3727 /* We know about this function */
3729 } else if (LineFullMatch (L, "\tjsr\tdeceaxy")) {
3730 /* We know about this function */
3732 } else if (LineFullMatch (L, "\tjsr\tincax1")) {
3733 /* We know about this function */
3735 } else if (LineFullMatch (L, "\tjsr\tincax2")) {
3736 /* We know about this function */
3738 } else if (LineFullMatch (L, "\tjsr\tinceaxy")) {
3739 /* We know about this function */
3741 } else if (LineFullMatch (L, "\tjsr\tladdeq")) {
3742 /* We know about this function */
3745 } else if (LineFullMatch (L, "\tjsr\tladdeqb")) {
3746 /* We know about this function */
3749 } else if (LineFullMatch (L, "\tjsr\tlbneg")) {
3750 /* We know about this function */
3753 } else if (LineFullMatch (L, "\tjsr\tldai")) {
3754 /* We know about this function */
3757 } else if (LineFullMatch (L, "\tjsr\tldaidx")) {
3758 /* We know about this function */
3760 } else if (LineFullMatch (L, "\tjsr\tldau00sp")) {
3761 /* We know about this function */
3765 } else if (LineFullMatch (L, "\tjsr\tldau0ysp")) {
3766 /* We know about this function */
3770 } else if (LineFullMatch (L, "\tjsr\tldaui")) {
3771 /* We know about this function */
3775 } else if (LineFullMatch (L, "\tjsr\tldaui0sp")) {
3779 } else if (LineFullMatch (L, "\tjsr\tldauidx")) {
3780 /* We know about this function */
3783 } else if (LineFullMatch (L, "\tjsr\tldauiysp")) {
3784 /* We know about this function */
3788 } else if (LineFullMatch (L, "\tjsr\tldax0sp")) {
3789 /* We know about this function */
3792 } else if (LineFullMatch (L, "\tjsr\tldaxi")) {
3793 /* We know about this function */
3796 } else if (LineFullMatch (L, "\tjsr\tldaxidx")) {
3797 /* We know about this function */
3800 } else if (LineFullMatch (L, "\tjsr\tldaxysp")) {
3801 /* We know about this function */
3804 } else if (LineFullMatch (L, "\tjsr\tldeaxi")) {
3805 /* We know about this function */
3808 } else if (LineFullMatch (L, "\tjsr\tldeaxidx")) {
3809 /* We know about this function */
3812 } else if (LineFullMatch (L, "\tjsr\tlsubeq")) {
3813 /* We know about this function */
3816 } else if (LineFullMatch (L, "\tjsr\tlsubeqb")) {
3817 /* We know about this function */
3820 } else if (LineFullMatch (L, "\tjsr\tnegax")) {
3821 /* We know about this function */
3823 } else if (LineFullMatch (L, "\tjsr\tnegeax")) {
3824 /* We know about this function */
3826 } else if (LineFullMatch (L, "\tjsr\tpush0")) {
3827 /* We know about this function */
3831 } else if (LineFullMatch (L, "\tjsr\tpush1")) {
3832 /* We know about this function */
3836 } else if (LineFullMatch (L, "\tjsr\tpush2")) {
3837 /* We know about this function */
3841 } else if (LineFullMatch (L, "\tjsr\tpush3")) {
3842 /* We know about this function */
3846 } else if (LineFullMatch (L, "\tjsr\tpush4")) {
3847 /* We know about this function */
3851 } else if (LineFullMatch (L, "\tjsr\tpush5")) {
3852 /* We know about this function */
3856 } else if (LineFullMatch (L, "\tjsr\tpush6")) {
3857 /* We know about this function */
3861 } else if (LineFullMatch (L, "\tjsr\tpush7")) {
3862 /* We know about this function */
3866 } else if (LineFullMatch (L, "\tjsr\tpusha")) {
3867 /* We know about this function */
3869 } else if (LineFullMatch (L, "\tjsr\tpusha0")) {
3870 /* We know about this function
3871 * If X is already zero, we may call pushax instead and save two
3875 L = ReplaceLine (L, "\tjsr\tpushax");
3879 } else if (LineFullMatch (L, "\tjsr\tpushax")) {
3880 /* We know about this function */
3882 } else if (LineFullMatch (L, "\tjsr\tpushaysp")) {
3883 /* We know about this function */
3886 } else if (LineFullMatch (L, "\tjsr\tpushc0")) {
3887 /* We know about this function */
3890 } else if (LineFullMatch (L, "\tjsr\tpushc1")) {
3891 /* We know about this function */
3894 } else if (LineFullMatch (L, "\tjsr\tpushc2")) {
3895 /* We know about this function */
3898 } else if (LineFullMatch (L, "\tjsr\tpushw")) {
3899 /* We know about this function (calls pushax) */
3902 } else if (LineFullMatch (L, "\tjsr\tpushw0sp")) {
3903 /* We know about this function(calls pushax) */
3906 } else if (LineFullMatch (L, "\tjsr\tpushwidx")) {
3907 /* We know about this function (calls pushax) */
3910 } else if (LineFullMatch (L, "\tjsr\tpushwysp")) {
3911 /* We know about this function (calls pushax) */
3914 } else if (LineFullMatch (L, "\tjsr\tresteax")) {
3915 /* We know about this function */
3917 } else if (LineFullMatch (L, "\tjsr\tsaveeax")) {
3918 /* We know about this function */
3919 /* Changes nothing */
3920 } else if (LineFullMatch (L, "\tjsr\tshrax1")) {
3921 /* We know about this function */
3923 } else if (LineFullMatch (L, "\tjsr\tshrax2")) {
3924 /* We know about this function */
3926 } else if (LineFullMatch (L, "\tjsr\tshrax3")) {
3927 /* We know about this function */
3929 } else if (LineFullMatch (L, "\tjsr\tshreax1")) {
3930 /* We know about this function */
3932 } else if (LineFullMatch (L, "\tjsr\tshreax2")) {
3933 /* We know about this function */
3935 } else if (LineFullMatch (L, "\tjsr\tshreax3")) {
3936 /* We know about this function */
3938 } else if (LineFullMatch (L, "\tjsr\tstaspp")) {
3939 /* We know about this function */
3941 } else if (LineFullMatch (L, "\tjsr\tstaxspp")) {
3942 /* We know about this function */
3944 } else if (LineFullMatch (L, "\tjsr\tstax0sp")) {
3945 /* We know about this function */
3947 } else if (LineFullMatch (L, "\tjsr\tstaxysp")) {
3948 /* We know about this function */
3950 } else if (LineFullMatch (L, "\tjsr\tsubeq0sp")) {
3951 /* We know about this function */
3954 } else if (LineFullMatch (L, "\tjsr\tsubeqysp")) {
3955 /* We know about this function */
3958 } else if (LineFullMatch (L, "\tjsr\ttosadda0")) {
3959 /* We know about this function */
3962 } else if (LineFullMatch (L, "\tjsr\ttosaddax")) {
3963 /* We know about this function */
3966 } else if (LineFullMatch (L, "\tjsr\ttosicmp")) {
3967 /* We know about this function */
3970 } else if (LineFullMatchX (L, Compares) >= 0) {
3973 } else if (LineFullMatchX (L, MakeBool) >= 0) {
3976 } else if (LineMatch (L, "\tjsr\t")) {
3977 /* Subroutine call, forget all register information */
3979 } else if (LineMatch (L, "\tlda\t")) {
3980 if (!RegAUsed (L) && !IsCondJump (NextInstruction (L))) {
3981 /* The value loaded is not used later, remove it */
3983 } else if (LineMatch (L, "\tlda\t(")) {
3984 if (IsXAddrMode (L)) {
3985 /* lda (zp,x) - if Y and X are both zero, replace by
3986 * load indirect y and save one cycle in some cases.
3988 if (X == 0 && Y == 0) {
3990 const char* S = L->Line + 6;
3992 strcpy (Buf, "\tlda\t(");
4000 L = ReplaceLine (L, Buf);
4003 /* In any case invalidate A */
4005 } else if (LineMatch (L, "\tlda\t#$")) {
4006 /* Immidiate load into A */
4007 NewVal = GetHexNum (L->Line + 7);
4009 /* Load has no effect */
4011 } else if (NewVal == X) {
4012 /* Requested value is already in X */
4013 L = ReplaceLine (L, "\ttxa");
4014 } else if (NewVal == Y) {
4015 /* Requested value is already in Y */
4016 L = ReplaceLine (L, "\ttya");
4017 } else if (CPU == CPU_65C02 && A != -1) {
4018 /* Try ina/dea operators of 65C02 */
4019 if (NewVal == ((A - 1) & 0xFF)) {
4020 L = ReplaceLine (L, "\tdea");
4021 } else if (NewVal == ((A + 1) & 0xFF)) {
4022 L = ReplaceLine (L, "\tina");
4025 /* Anyway, the new value is now in A */
4028 /* Memory load into A */
4031 } else if (LineMatch (L, "\tldax\t")) {
4032 /* Memory load into A and X */
4034 } else if (LineMatch (L, "\tldx\t")) {
4035 if (!RegXUsed (L) && !IsCondJump (NextInstruction (L))) {
4036 /* The value loaded is not used later, remove it */
4038 } else if (LineMatch (L, "\tldx\t#$")) {
4039 /* Immidiate load into X */
4040 NewVal = GetHexNum (L->Line + 7);
4042 /* Load has no effect */
4044 } else if (NewVal == A) {
4045 /* Requested value is already in A */
4046 L = ReplaceLine (L, "\ttax");
4047 } else if (X != -1 && NewVal == ((X + 1) & 0xFF)) {
4048 /* Requested value is one more than current contents */
4049 L = ReplaceLine (L, "\tinx");
4050 } else if (X != -1 && NewVal == ((X - 1) & 0xFF)) {
4051 /* Requested value is one less than current contents */
4052 L = ReplaceLine (L, "\tdex");
4054 /* Anyway, the new value is now in X */
4057 /* Memory load into X */
4060 } else if (LineMatch (L, "\tldy\t")) {
4061 if (!RegYUsed (L) && !IsCondJump (NextInstruction (L))) {
4062 /* The value loaded is not used later, remove it */
4064 } else if (LineMatch (L, "\tldy\t#$")) {
4065 /* Immidiate load into Y */
4066 NewVal = GetHexNum (L->Line + 7);
4068 /* Load has no effect */
4070 } else if (NewVal == A) {
4071 /* Requested value is already in A */
4072 L = ReplaceLine (L, "\ttay");
4073 } else if (Y != -1 && NewVal == ((Y + 1) & 0xFF)) {
4074 /* Requested value is one more than current contents */
4075 L = ReplaceLine (L, "\tiny");
4076 } else if (Y != -1 && NewVal == ((Y - 1) & 0xFF)) {
4077 /* Requested value is one less than current contents */
4078 L = ReplaceLine (L, "\tdey");
4080 /* Anyway, the new value is now in Y */
4083 /* Memory load into Y */
4086 } else if (LineFullMatch (L, "\tlsr\ta")) {
4090 } else if (LineMatch (L, "\tora\t#$")) {
4092 A |= GetHexNum (L->Line + 7);
4094 } else if (LineMatch (L, "\tora\t")) {
4096 } else if (LineFullMatch (L, "\tpla")) {
4098 } else if (LineFullMatch (L, "\trol\ta")) {
4100 } else if (LineFullMatch (L, "\tror\ta")) {
4102 } else if (LineFullMatch (L, "\trts")) {
4104 } else if (LineFullMatch (L, "\trti")) {
4106 } else if (LineMatch (L, "\tsbc\t")) {
4107 if (CPU == CPU_65C02 && Y == 0 && L->Line[5] == '(' && IsYAddrMode(L)) {
4108 L->Line[strlen(L->Line)-2] = '\0';
4111 } else if (CPU == CPU_65C02 && LineMatch (L, "\tst")) {
4112 /* Try to replace by stz if possible */
4113 if (A == 0 && LineMatch (L, "\tsta\t")) {
4114 /* Not indirect and not Y allowed */
4115 if (L->Line[5] != '(' && !IsYAddrMode (L)) {
4118 } else if (X == 0 && LineMatch (L, "\tstx\t")) {
4119 /* absolute,y not allowed */
4120 if (!IsYAddrMode (L)) {
4123 } else if (Y == 0 && LineMatch (L, "\tsty\t")) {
4124 /* sty and stz share all addressing modes */
4127 } else if (LineFullMatch (L, "\ttax")) {
4128 if (A != -1 && X == A) {
4129 /* Load has no effect */
4134 } else if (LineFullMatch (L, "\ttay")) {
4135 if (A != -1 && Y == A) {
4136 /* Load has no effect */
4141 } else if (LineFullMatch (L, "\ttsx")) {
4143 } else if (LineFullMatch (L, "\ttxa")) {
4144 if (X != -1 && A == X) {
4145 /* Load has no effect */
4150 } else if (LineFullMatch (L, "\ttya")) {
4151 if (Y != -1 && A == Y) {
4152 /* Load has no effect */
4159 /* Set to next line, handle deletions */
4160 L2 = NextCodeSegLine (L);
4168 /* Skip the label */
4169 L = NextCodeSegLine (L);
4176 static void OptBlocks (void)
4177 /* Optimize the register contents inside basic blocks */
4179 Line* L = FirstCode;
4181 L = OptOneBlock (L);
4187 static void OptJumps (void)
4188 /* Optimize jumps */
4190 static const char* Jumps [] = {
4200 Line* L = FirstCode;
4202 int I = LineMatchX (L, Jumps);
4204 Line* Target = GetTargetLine (L->Line+5);
4205 if (Target->Index > L->Index) {
4206 /* This is a forward jump. Backward jumps are handled
4207 * automagically by the assembler.
4209 unsigned Distance = GetJumpDistance (L, Target);
4210 if (Distance < 123) { /* Safety */
4211 L->Line [1] = 'b'; /* Make a short branch */
4212 L->Size = 2; /* Set new size */
4216 L = NextCodeLine (L);
4219 /* Special treatment for jumps on the 65C02 */
4220 if (CPU == CPU_65C02) {
4222 Line* L = FirstCode;
4224 if (LineMatch (L, "\tjmp\tL")) {
4225 Line* Target = GetTargetLine (L->Line+5);
4226 unsigned Distance = GetJumpDistance (L, Target);
4227 if (Distance < 123) { /* Safety */
4228 L->Line [1] = 'b'; /* Make a short branch */
4231 L->Size = 2; /* Set new size */
4234 L = NextCodeLine (L);
4242 static void OptRTS (void)
4243 /* Change sequences of jsr XXX/rts to jmp XXX */
4245 Line* L = FirstCode;
4247 if (LineMatch (L, "\tjsr\t")) {
4248 /* This is a jsr, get the next instruction */
4249 Line* L2 = NextCodeLine (L);
4250 if (L2 && LineFullMatch (L2, "\trts")) {
4251 /* We found a sequence */
4257 /* Try the next line */
4258 L = NextCodeLine (L);
4264 /*****************************************************************************/
4266 /*****************************************************************************/
4270 static void OptOnePass (unsigned long Flag, void (*F) (void))
4271 /* Call one optimizer pass if enabled */
4273 if ((OptDisable & Flag) == 0) {
4275 } else if (Verbose || Debug) {
4276 printf ("Optimizer pass %04lX skipped\n", Flag);
4282 void OptDoOpt (void)
4283 /* Run the optimizer over the collected stuff */
4285 /* Find and remember the first line of code */
4288 /* Mark all lines inside the code segment */
4291 /* Create a list of all local labels for fast access */
4294 /* Ok, now start the real optimizations */
4296 /* Optimize compares - first step */
4297 OptOnePass (0x0001, OptCompares1);
4299 /* Remove dead jumps */
4300 OptOnePass (0x0002, OptDeadJumps);
4302 /* Remove unnecessary loads */
4303 OptOnePass (0x0004, OptLoads);
4305 /* Remove unnecessary register loads */
4306 OptOnePass (0x0008, OptRegLoads);
4308 /* Optimize stores through pointers */
4309 OptOnePass (0x0010, OptPtrOps);
4311 /* Optimize use of register variables */
4312 OptOnePass (0x0020, OptRegVars);
4314 /* Remove jump cascades - must be used before OptNeg */
4315 OptOnePass (0x0040, OptDoubleJumps);
4317 /* Remove unnecessary boolean negates */
4318 OptOnePass (0x0080, OptNeg);
4320 /* Replace jumps to an RTS by an RTS */
4321 OptOnePass (0x0100, OptJumpRTS);
4323 /* Optimize boolean transforms */
4324 OptOnePass (0x0200, OptBoolTransforms);
4326 /* Optimize compares */
4327 OptOnePass (0x0400, OptCompares2);
4329 /* Remove unnecessary tests */
4330 OptOnePass (0x0800, OptTests);
4332 /* Optimize several triples */
4333 OptOnePass (0x1000, OptTriples);
4335 /* Optimize basic blocks */
4336 OptOnePass (0x2000, OptBlocks);
4338 /* Remove unnecessary register loads (another pass) */
4339 OptOnePass (0x0008, OptRegLoads);
4341 /* Optimize jumps */
4342 OptOnePass (0x4000, OptJumps);
4344 /* Optimize jsr/rts sequences */
4345 OptOnePass (0x8000, OptRTS);