Imported Upstream version 2.9.0
[debian/cc1111] / src / mcs51 / peep.c
1 /*-------------------------------------------------------------------------
2   peep.c - source file for peephole optimizer helper functions
3
4   Written By -  Bernhard Held
5
6   This program is free software; you can redistribute it and/or modify it
7   under the terms of the GNU General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20   In other words, you are welcome to use, share and improve this program.
21   You are forbidden to forbid anyone else to use, share and improve
22   what you give them.   Help stamp out software-hoarding!
23 -------------------------------------------------------------------------*/
24
25 #include "common.h"
26 #include "ralloc.h"
27
28 #define D(x) x
29 #define DEADMOVEERROR() do {werror(E_INTERNAL_ERROR, __FILE__, __LINE__, "error in deadmove");} while(0)
30
31 typedef enum
32 {
33   S4O_FOUNDOPCODE,
34   S4O_PUSHPOP,
35   S4O_CONDJMP,
36   S4O_WR_OP,
37   S4O_RD_OP,
38   S4O_TERM,
39   S4O_VISITED,
40   S4O_ABORT,
41   S4O_CONTINUE
42 } S4O_RET;
43
44 static struct
45 {
46   lineNode *head;
47 } _G;
48
49 /*-----------------------------------------------------------------*/
50 /* univisitLines - clear "visited" flag in all lines               */
51 /*-----------------------------------------------------------------*/
52 static void
53 unvisitLines (lineNode *pl)
54 {
55   for (; pl; pl = pl->next)
56     pl->visited = FALSE;
57 }
58
59 /*-----------------------------------------------------------------*/
60 /* cleanLabelRef - clear label jump-counter and pass-flag          */
61 /*-----------------------------------------------------------------*/
62 static void
63 cleanLabelRef (void)
64 {
65   int key;
66   labelHashEntry *entry;
67
68   if (!labelHash)
69     return;
70   for (entry = (labelHashEntry *) hTabFirstItem (labelHash, &key);
71        entry;
72        entry = (labelHashEntry *) hTabNextItem (labelHash, &key))
73     {
74       entry->passedLabel = FALSE;
75       entry->jmpToCount = 0;
76     }
77 }
78
79 /*-----------------------------------------------------------------*/
80 /* checkLabelRef - check all entries in labelHash                  */
81 /* The path from 'pop' to 'push' must be the only possible path.   */
82 /* There must not be any paths in or out of this path.             */
83 /* This is checked by counting the label references.               */
84 /*-----------------------------------------------------------------*/
85 static bool
86 checkLabelRef (void)
87 {
88   int key;
89   labelHashEntry *entry;
90
91   if (!labelHash)
92     {
93       /* no labels at all: no problems ;-) */
94       return TRUE;
95     }
96
97   for (entry = (labelHashEntry *) hTabFirstItem (labelHash, &key);
98        entry;
99        entry = (labelHashEntry *) hTabNextItem (labelHash, &key))
100     {
101
102       /* In our path we passed a label,
103          but we didn't meet all references (jumps) to this label.
104          This means that the code jumps from outside into this path. */
105       if (entry->passedLabel &&
106           entry->jmpToCount != entry->refCount)
107         {
108           return FALSE;
109         }
110
111       /* In our path we jumped to (referenced) a label,
112          but we we didn't pass it.
113          This means that there's a code path into our path. */
114       if (!entry->passedLabel &&
115           entry->jmpToCount != 0)
116         {
117           return FALSE;
118         }
119     }
120   return TRUE;
121 }
122
123 /*-----------------------------------------------------------------*/
124 /* setLabelRefPassedLabel - set flag "passedLabel" in entry        */
125 /* of the list labelHash                                           */
126 /*-----------------------------------------------------------------*/
127 static bool
128 setLabelRefPassedLabel (const char *label)
129 {
130   labelHashEntry *entry;
131
132   entry = getLabelRef (label, _G.head);
133   if (!entry)
134     return FALSE;
135   entry->passedLabel = TRUE;
136   return TRUE;
137 }
138
139 /*-----------------------------------------------------------------*/
140 /* incLabelJmpToCount - increment counter "jmpToCount" in entry    */
141 /* of the list labelHash                                           */
142 /*-----------------------------------------------------------------*/
143 static bool
144 incLabelJmpToCount (const char *label)
145 {
146   labelHashEntry *entry;
147
148   entry = getLabelRef (label, _G.head);
149   if (!entry)
150     return FALSE;
151   entry->jmpToCount++;
152   return TRUE;
153 }
154
155 /*-----------------------------------------------------------------*/
156 /* findLabel -                                                     */
157 /* 1. extracts label in the opcode pl                              */
158 /* 2. increment "label jump-to count" in labelHash                 */
159 /* 3. search lineNode with label definition and return it          */
160 /*-----------------------------------------------------------------*/
161 static lineNode *
162 findLabel (const lineNode *pl)
163 {
164   char *p;
165   lineNode *cpl;
166
167   /* 1. extract label in opcode */
168
169   /* In each mcs51 jumping opcode the label is at the end of the opcode */
170   p = strlen (pl->line) - 1 + pl->line;
171
172   /* scan backward until ',' or '\t' */
173   for (; p > pl->line; p--)
174     if (*p == ',' || *p == '\t')
175       break;
176
177   /* sanity check */
178   if (p == pl->line)
179     {
180       DEADMOVEERROR();
181       return NULL;
182     }
183
184   /* skip ',' resp. '\t' */
185   ++p;
186
187   /* 2. increment "label jump-to count" */
188   if (!incLabelJmpToCount (p))
189     return NULL;
190
191   /* 3. search lineNode with label definition and return it */
192   for (cpl = _G.head; cpl; cpl = cpl->next)
193     {
194       if (   cpl->isLabel
195           && strcmp (p, cpl->line) == 0)
196         {
197           return cpl;
198         }
199     }
200   return NULL;
201 }
202
203 /*-----------------------------------------------------------------*/
204 /* isFunc - returns TRUE if it's a CALL or PCALL (not _gptrget())  */
205 /*-----------------------------------------------------------------*/
206 static bool
207 isFunc (const lineNode *pl)
208 {
209   if (pl && pl->ic)
210     {
211       if (   pl->ic->op == CALL
212           || pl->ic->op == PCALL)
213         return TRUE;
214     }
215   return FALSE;
216 }
217
218 /*-----------------------------------------------------------------*/
219 /* termScanAtFunc - returns S4O_TERM if it's a 'normal' function   */
220 /* call and it's a 'caller save'. returns S4O_CONTINUE if it's     */
221 /* 'callee save' or 'naked'. returns S4O_ABORT if it's 'banked'    */
222 /* uses the register for the destination.                          */
223 /*-----------------------------------------------------------------*/
224 static S4O_RET
225 termScanAtFunc (const lineNode *pl, int rIdx)
226 {
227   sym_link *ftype;
228
229   if (!isFunc (pl))
230     return S4O_CONTINUE;
231   // let's assume calls to literally given locations use the default
232   // most notably :  (*(void (*)()) 0) ();  see bug 1749275
233   if (IS_VALOP (IC_LEFT (pl->ic)))
234     return !options.all_callee_saves;
235
236   ftype = OP_SYM_TYPE(IC_LEFT(pl->ic));
237   if (IS_FUNCPTR (ftype))
238     ftype = ftype->next;
239   if (FUNC_CALLEESAVES(ftype))
240     return S4O_CONTINUE;
241   if (FUNC_ISNAKED(ftype))
242     return S4O_CONTINUE;
243   if (FUNC_BANKED(ftype) &&
244       ((rIdx == R0_IDX) || (rIdx == R1_IDX) || (rIdx == R2_IDX)))
245     return S4O_ABORT;
246   return S4O_TERM;
247 }
248
249 /*-----------------------------------------------------------------*/
250 /* scan4op - "executes" and examines the assembler opcodes,        */
251 /* follows conditional and un-conditional jumps.                   */
252 /* Moreover it registers all passed labels.                        */
253 /*                                                                 */
254 /* Parameter:                                                      */
255 /*    lineNode **pl                                                */
256 /*       scanning starts from pl;                                  */
257 /*       pl also returns the last scanned line                     */
258 /*    const char *pReg                                             */
259 /*       points to a register (e.g. "ar0"). scan4op() tests for    */
260 /*       read or write operations with this register               */
261 /*    const char *untilOp                                          */
262 /*       points to NULL or a opcode (e.g. "push").                 */
263 /*       scan4op() returns if it hits this opcode.                 */
264 /*    lineNode **plCond                                            */
265 /*       If a conditional branch is met plCond points to the       */
266 /*       lineNode of the conditional branch                        */
267 /*                                                                 */
268 /* Returns:                                                        */
269 /*    S4O_ABORT                                                    */
270 /*       on error                                                  */
271 /*    S4O_VISITED                                                  */
272 /*       hit lineNode with "visited" flag set: scan4op() already   */
273 /*       scanned this opcode.                                      */
274 /*    S4O_FOUNDOPCODE                                              */
275 /*       found opcode and operand, to which untilOp and pReg are   */
276 /*       pointing to.                                              */
277 /*    S4O_RD_OP, S4O_WR_OP                                         */
278 /*       hit an opcode reading or writing from pReg                */
279 /*    S4O_PUSHPOP                                                  */
280 /*       hit a "push" or "pop" opcode                              */
281 /*    S4O_CONDJMP                                                  */
282 /*       hit a conditional jump opcode. pl and plCond return the   */
283 /*       two possible branches.                                    */
284 /*    S4O_TERM                                                     */
285 /*       acall, lcall, ret and reti "terminate" a scan.            */
286 /*-----------------------------------------------------------------*/
287 static S4O_RET
288 scan4op (lineNode **pl, const char *pReg, const char *untilOp,
289          lineNode **plCond)
290 {
291   char *p;
292   int len;
293   bool isConditionalJump;
294   int rIdx;
295   S4O_RET ret;
296
297   /* pReg points to e.g. "ar0"..."ar7" */
298   len = strlen (pReg);
299
300   /* get index into pReg table */
301   for (rIdx = 0; rIdx < mcs51_nRegs; ++rIdx)
302     if (strcmp (regs8051[rIdx].name, pReg + 1) == 0)
303       break;
304
305   /* sanity check */
306   if (rIdx >= mcs51_nRegs)
307     {
308       DEADMOVEERROR();
309       return S4O_ABORT;
310     }
311
312   for (; *pl; *pl = (*pl)->next)
313     {
314       if (!(*pl)->line || (*pl)->isDebug || (*pl)->isComment)
315         continue;
316
317       /* don't optimize across inline assembler,
318          e.g. isLabel doesn't work there */
319       if ((*pl)->isInline)
320         return S4O_ABORT;
321
322       if ((*pl)->visited)
323         return S4O_VISITED;
324       (*pl)->visited = TRUE;
325
326       /* found untilOp? */
327       if (untilOp && strncmp ((*pl)->line, untilOp, strlen (untilOp)) == 0)
328         {
329           p = (*pl)->line + strlen (untilOp);
330           if (*p == '\t' && strncmp (p + 1, pReg, len) == 0)
331             return S4O_FOUNDOPCODE;
332           else
333             {
334               /* found untilOp but without our pReg */
335               return S4O_ABORT;
336             }
337         }
338
339       /* found pReg? */
340       p = strchr ((*pl)->line, '\t');
341       if (p)
342         {
343           /* skip '\t' */
344           p++;
345
346           /* course search */
347           if (strstr (p, pReg + 1))
348             {
349               /* ok, let's have a closer look */
350
351               /* does opcode read from pReg? */
352               if (bitVectBitValue (port->peep.getRegsRead ((*pl)), rIdx))
353                 return S4O_RD_OP;
354               /* does opcode write to pReg? */
355               if (bitVectBitValue (port->peep.getRegsWritten ((*pl)), rIdx))
356                 return S4O_WR_OP;
357
358               /* we can get here, if the register name is
359                  part of a variable name: ignore it */
360             }
361         }
362
363       /* found label? */
364       if ((*pl)->isLabel)
365         {
366           const char *start;
367           char label[SDCC_NAME_MAX + 1];
368           int len;
369
370           if (!isLabelDefinition ((*pl)->line, &start, &len, FALSE))
371             return S4O_ABORT;
372           memcpy (label, start, len);
373           label[len] = '\0';
374           /* register passing this label */
375           if (!setLabelRefPassedLabel (label))
376             {
377               DEADMOVEERROR();
378               return S4O_ABORT;
379             }
380           continue;
381         }
382
383       /* branch or terminate? */
384       isConditionalJump = FALSE;
385       switch ((*pl)->line[0])
386         {
387           case 'a':
388             if (strncmp ("acall", (*pl)->line, 5) == 0)
389               {
390                 /* for comments see 'lcall' */
391                 ret = termScanAtFunc (*pl, rIdx);
392                 if (ret != S4O_CONTINUE)
393                   return ret;
394                 break;
395               }
396             if (strncmp ("ajmp", (*pl)->line, 4) == 0)
397               {
398                 *pl = findLabel (*pl);
399                 if (!*pl)
400                   return S4O_ABORT;
401               }
402             break;
403           case 'c':
404             if (strncmp ("cjne", (*pl)->line, 4) == 0)
405               {
406                 isConditionalJump = TRUE;
407                 break;
408               }
409             break;
410           case 'd':
411             if (strncmp ("djnz", (*pl)->line, 4) == 0)
412               {
413                 isConditionalJump = TRUE;
414                 break;
415               }
416             break;
417           case 'j':
418             if (strncmp ("jmp", (*pl)->line, 3) == 0)
419               /* "jmp @a+dptr": no chance to trace execution */
420               return S4O_ABORT;
421             if (strncmp ("jc",  (*pl)->line, 2) == 0 ||
422                 strncmp ("jnc", (*pl)->line, 3) == 0 ||
423                 strncmp ("jz",  (*pl)->line, 2) == 0 ||
424                 strncmp ("jnz", (*pl)->line, 3) == 0)
425               {
426                 isConditionalJump = TRUE;
427                 break;
428               }
429             if (strncmp ("jbc", (*pl)->line, 3) == 0 ||
430                 strncmp ("jb",  (*pl)->line, 2) == 0 ||
431                 strncmp ("jnb", (*pl)->line, 3) == 0)
432               {
433                 isConditionalJump = TRUE;
434                 break;
435               }
436             break;
437           case 'l':
438             if (strncmp ("lcall", (*pl)->line, 5) == 0)
439               {
440                 ret = termScanAtFunc (*pl, rIdx);
441                 /* If it's a 'normal' 'caller save' function call, all
442                    registers have been saved until the 'lcall'. The
443                    'life range' of all registers end at the lcall,
444                    and we can terminate our search.
445                  * If the function is 'banked', the registers r0, r1 and r2
446                    are used to tell the trampoline the destination. After
447                    that their 'life range' ends just like the other registers.
448                  * If it's a 'callee save' function call, registers are saved
449                    by the callee. We've got no information, if the register
450                    might live beyond the lcall. Therefore we've to continue
451                    the search.
452                 */
453                 if (ret != S4O_CONTINUE)
454                   return ret;
455                 break;
456               }
457             if (strncmp ("ljmp", (*pl)->line, 4) == 0)
458               {
459                 *pl = findLabel (*pl);
460                 if (!*pl)
461                   return S4O_ABORT;
462               }
463             break;
464           case 'p':
465             if (strncmp ("pop", (*pl)->line, 3) == 0 ||
466                 strncmp ("push", (*pl)->line, 4) == 0)
467               return S4O_PUSHPOP;
468             break;
469           case 'r':
470             if (strncmp ("reti", (*pl)->line, 4) == 0)
471               return S4O_TERM;
472
473             if (strncmp ("ret", (*pl)->line, 3) == 0)
474               {
475                 /* pcall uses 'ret' */
476                 if (isFunc (*pl))
477                   {
478                     /* for comments see 'lcall' */
479                     ret = termScanAtFunc (*pl, rIdx);
480                     if (ret != S4O_CONTINUE)
481                       return ret;
482                     break;
483                   }
484
485                 /* it's a normal function return */
486                 return S4O_TERM;
487               }
488             break;
489           case 's':
490             if (strncmp ("sjmp", (*pl)->line, 4) == 0)
491               {
492                 *pl = findLabel (*pl);
493                 if (!*pl)
494                   return S4O_ABORT;
495               }
496             break;
497           default:
498             break;
499         } /* switch ((*pl)->line[0]) */
500
501       if (isConditionalJump)
502         {
503           *plCond = findLabel (*pl);
504           if (!*plCond)
505             return S4O_ABORT;
506           return S4O_CONDJMP;
507         }
508     } /* for (; *pl; *pl = (*pl)->next) */
509   return S4O_ABORT;
510 }
511
512 /*-----------------------------------------------------------------*/
513 /* doPushScan - scan through area 1. This small wrapper handles:   */
514 /* - action required on different return values                    */
515 /* - recursion in case of conditional branches                     */
516 /*-----------------------------------------------------------------*/
517 static bool
518 doPushScan (lineNode **pl, const char *pReg)
519 {
520   lineNode *plConditional, *pushPl = NULL;
521
522   for (;; *pl = (*pl)->next)
523     {
524       switch (scan4op (pl, pReg, "push", &plConditional))
525         {
526           case S4O_FOUNDOPCODE:
527             /* this is what we're looking for */
528             return TRUE;
529           case S4O_VISITED:
530             if (!pushPl)
531               {
532                 DEADMOVEERROR();
533                 return FALSE;
534               }
535             *pl = pushPl;
536             /* already checked */
537             return TRUE;
538           case S4O_CONDJMP:
539             /* two possible destinations: recurse */
540               {
541                 lineNode *pushPl2 = plConditional;
542
543                 if (!doPushScan (&pushPl2, pReg))
544                   return FALSE;
545                 pushPl = pushPl2;
546               }
547             continue;
548           default:
549             return FALSE;
550         }
551     }
552 }
553
554 /*-----------------------------------------------------------------*/
555 /* doTermScan - scan through area 2. This small wrapper handles:   */
556 /* - action required on different return values                    */
557 /* - recursion in case of conditional branches                     */
558 /*-----------------------------------------------------------------*/
559 static bool
560 doTermScan (lineNode **pl, const char *pReg)
561 {
562   lineNode *plConditional;
563
564   for (;; *pl = (*pl)->next)
565     {
566       switch (scan4op (pl, pReg, NULL, &plConditional))
567         {
568           case S4O_TERM:
569           case S4O_VISITED:
570           case S4O_WR_OP:
571             /* all these are terminating condtions */
572             return TRUE;
573           case S4O_PUSHPOP:
574             /* don't care, go on */
575             continue;
576           case S4O_CONDJMP:
577             /* two possible destinations: recurse */
578               {
579                 lineNode *pl2 = plConditional;
580
581                 if (!doTermScan (&pl2, pReg))
582                   return FALSE;
583               }
584             continue;
585           case S4O_RD_OP:
586           default:
587             /* no go */
588             return FALSE;
589         }
590     }
591 }
592
593 /*-----------------------------------------------------------------*/
594 /* removeDeadPopPush - remove pop/push pair if possible            */
595 /*-----------------------------------------------------------------*/
596 static bool
597 removeDeadPopPush (const char *pReg, lineNode *currPl, lineNode *head)
598 {
599   lineNode *pushPl, *pl;
600
601   /* A pop/push pair can be removed, if these criteria are met
602      (ar0 is just an example here, ar0...ar7 are possible):
603
604      pop ar0
605
606       ; area 1
607
608       ; There must not be in area 1:
609       ;    - read or write access of ar0
610       ;    - "acall", "lcall", "pop", "ret", "reti" or "jmp @a+dptr" opcodes
611       ;    - "push" opcode, which doesn't push ar0 
612       ;    - inline assembly
613       ;    - a jump in or out of area 1 (see checkLabelRef())
614
615       ; Direct manipulation of sp is not detected. This isn't necessary
616       ; as long as sdcc doesn't emit such code in area 1.
617
618       ; area 1 must be terminated by a:
619      push ar0
620
621       ; area 2
622
623       ; There must not be:
624       ;    - read access of ar0
625       ;    - "jmp @a+dptr" opcode
626       ;    - inline assembly
627       ;    - a jump in or out of area 2 (see checkLabelRef())
628
629       ; An "acall", "lcall" (not callee save), "ret" (not PCALL with
630       ; callee save), "reti" or write access of r0 terminate
631       ; the search, and the "mov r0,a" can safely be removed.
632   */
633
634   /* area 1 */
635   pushPl = currPl->next;
636   if (!doPushScan (&pushPl, pReg))
637     return FALSE;
638
639   if (!checkLabelRef())
640     return FALSE;
641
642   /* area 2 */
643   pl = pushPl->next;
644   if (!doTermScan (&pl, pReg))
645     return FALSE;
646   if (!checkLabelRef())
647     return FALSE;
648
649   /* Success! */
650   if (options.noPeepComments)
651     {
652       /* remove pushPl from list */
653       pushPl->prev->next = pushPl->next;
654       pushPl->next->prev = pushPl->prev;
655     }
656   else
657     {
658       /* replace 'push ar0' by comment */
659       #define STR ";\tPeephole\tpush %s removed"
660       int size = sizeof(STR) + 2;
661
662       pushPl->line = Safe_alloc (size);
663       SNPRINTF (pushPl->line, size, STR, pReg);
664       pushPl->isComment = TRUE;
665     }
666
667   /* 'pop ar0' will be removed by peephole framework after returning TRUE */
668   return TRUE;
669 }
670
671 /*-----------------------------------------------------------------*/
672 /* removeDeadMove - remove superflous 'mov r%1,%2'                 */
673 /*-----------------------------------------------------------------*/
674 static bool
675 removeDeadMove (const char *pReg, lineNode *currPl, lineNode *head)
676 {
677   lineNode *pl;
678
679   /* "mov r0,a" can be removed, if these criteria are met
680      (r0 is just an example here, r0...r7 are possible):
681
682       ; There must not be:
683       ;    - read access of r0
684       ;    - "jmp @a+dptr" opcode
685       ;    - inline assembly
686       ;    - a jump in or out of this area (see checkLabelRef())
687
688       ; An "acall", "lcall" (not callee save), "ret" (not PCALL with
689       ; callee save), "reti" or write access of r0 terminate
690       ; the search, and the "mov r0,a" can safely be removed.
691   */
692   pl = currPl->next;
693   if (!doTermScan (&pl, pReg))
694     return FALSE;
695
696   if (!checkLabelRef())
697     return FALSE;
698
699   return TRUE;
700 }
701
702 /*-----------------------------------------------------------------*/
703 /* mcs51DeadMove - dispatch condition deadmove between             */
704 /* - remove pop/push                                               */
705 /* - remove mov r%1,%2                                             */
706 /*-----------------------------------------------------------------*/
707 bool
708 mcs51DeadMove (const char *reg, lineNode *currPl, lineNode *head)
709 {
710   char pReg[5] = "ar";
711
712   _G.head = head;
713   strcat (pReg, reg);
714
715   unvisitLines (_G.head);
716   cleanLabelRef();
717
718   if (strncmp (currPl->line, "pop", 3) == 0)
719     return removeDeadPopPush (pReg, currPl, head);
720   else if (   strncmp (currPl->line, "mov", 3) == 0
721            && (currPl->line[3] == ' ' || currPl->line[3] == '\t'))
722     return removeDeadMove (pReg, currPl, head);
723   else
724     {
725       fprintf (stderr, "Error: "
726                        "peephole rule with condition deadMove "
727                        "used with unknown opocde:\n"
728                        "\t%s\n", currPl->line);
729       return FALSE;
730     }
731 }