Imported Upstream version 2.9.0
[debian/cc1111] / src / mcs51 / rtrack.c
1 /*-------------------------------------------------------------------------
2   rtrack.c - tracking content of registers on an mcs51
3
4   Copyright 2007 Frieder Ferlemann (Frieder Ferlemann AT web.de)
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2 of the License, or
9   (at your option) any 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19 -------------------------------------------------------------------------*/
20
21 /*-------------------------------------------------------------------------
22   Status:
23     - passes regression test suite, still bugs are likely
24     - only active if environment variable SDCC_RTRACK is set
25
26   Missed opportunities:
27     - does not track symbols as in "mov a,#_my_int" or "mov a,#(_my_int+1)"
28     - only used with moves to acc so chances to use: "inc dptr",
29       "inc r2", "add a,r2" or "mov r2,a" would not be detected)
30     - a label causes loss of tracking (no handling of information of blocks
31       known to follow/preceed the current block)
32     - not used in aopGet or genRet
33     - does not track which registers are known to be unchanged within
34       a function (would not have to be saved when calling the function)
35 -------------------------------------------------------------------------*/
36
37
38 #include <stdio.h>
39 #include <string.h>
40 #include "SDCCglobl.h"
41
42 #include "common.h"
43 #include "ralloc.h"
44 #include "gen.h"
45
46 #define DEBUG(x)
47 //#define DEBUG(x) x
48
49 #define D(x) do if (options.verboseAsm) {x;} while(0)
50
51 #define REGS8051_SET(idx,val) do{ \
52                                   regs8051[idx].value = (val) & 0xff; \
53                                   regs8051[idx].valueKnown = 1; \
54                                   DEBUG(printf("%s:0x%02x\n",regs8051[idx].name, \
55                                                        regs8051[idx].value);) \
56                               } while(0)
57
58 #define REGS8051_UNSET(idx)   do{ \
59                                   regs8051[idx].valueKnown = 0; \
60                                   DEBUG(printf("%s:*\n",regs8051[idx].name);) \
61                               } while(0)
62
63 /* r0..r7 are not in numerical order in struct regs: r2..r7,r0,r1 */
64 #define Rx_NUM_TO_IDX(num) (R2_IDX+((num-2)&0x07))
65
66
67 /* move this (or rtrackGetLit() and rtrackMoveALit()
68    elsewhere? stealing emitcode from gen.c */
69 void emitcode (const char *inst, const char *fmt,...);
70
71
72 static int enable = -1;
73
74 /*
75 static void dumpAll()
76 {
77   unsigned int i;
78   unsigned int nl=0;
79
80   for (i=0; i<END_IDX; i++)
81     {
82        if (regs8051[i].valueKnown)
83          {
84            if (!nl)
85              {
86                DEBUG(printf("know:");)
87              }
88            DEBUG(printf(" %s:0x%02x",regs8051[i].name,regs8051[i].value);)
89            nl = 1;
90          }
91     }
92   if (nl)
93     {
94       DEBUG(printf("\n");)
95     }
96 }
97 */
98
99
100 static void invalidateAllRx()
101 {
102   //DEBUG(dumpAll();)
103   DEBUG(printf("R0..7:*\n");)
104   regs8051[R2_IDX].valueKnown = 0;
105   regs8051[R3_IDX].valueKnown = 0;
106   regs8051[R4_IDX].valueKnown = 0;
107   regs8051[R5_IDX].valueKnown = 0;
108   regs8051[R6_IDX].valueKnown = 0;
109   regs8051[R7_IDX].valueKnown = 0;
110   regs8051[R0_IDX].valueKnown = 0;
111   regs8051[R1_IDX].valueKnown = 0;
112 }
113
114 static void invalidateAll()
115 {
116   DEBUG(printf("All:* ");)
117   invalidateAllRx();
118   regs8051[DPL_IDX].valueKnown = 0;
119   regs8051[DPH_IDX].valueKnown = 0;
120   regs8051[B_IDX].valueKnown = 0;
121   regs8051[A_IDX].valueKnown = 0;
122 }
123
124 static regs * getReg(const char *str)
125 {
126   char *s;
127   int regNum;
128
129   regNum = strtol (str, &s, 16);
130   if (s == str+1)
131     {
132       return &regs8051[Rx_NUM_TO_IDX(regNum)];
133     }
134   return NULL;
135 }
136
137 /* tracking values within registers by looking
138    at the line passed to the assembler.
139    Tries to keep regs8051[] up to date */
140 void rtrackUpdate (const char *line)
141 {
142   if (enable == -1)
143     enable = (NULL != getenv("SDCC_RTRACK"));
144
145   if (!enable ||
146       *line == ';' ||                 /* comment */
147       (NULL != strstr( line, "==."))) /* dirty check for _G.debugLine */
148     return;                           /* nothing to do */
149
150   DEBUG(printf("%s\n",line);)
151
152   if (!strncmp (line,"mov",3))
153     {
154       /* check literal mov to accumulator */
155       if(!strncmp (line,"mov\ta,#0x",9))
156         {
157           char *s;
158           int value;
159
160           value = strtol (line+7, &s, 16);
161           if (s != line+7)
162               REGS8051_SET (A_IDX, value); /* valid hex found */
163           else
164               REGS8051_UNSET (A_IDX); /* probably a symbol (not handled) */
165
166           return;
167         }
168
169       if (!strncmp (line,"mov\ta,r",7))
170         {
171           /* handle mov from Rx if Rx is known */
172           regs *r = getReg(line+7);
173           if (r && r->valueKnown)
174             {
175               REGS8051_SET (A_IDX, r->value);
176               return;
177             }
178           REGS8051_UNSET (A_IDX);
179           return;
180         }
181
182       if (!strncmp (line,"mov\ta",5))
183         {
184           REGS8051_UNSET (A_IDX);
185           return;
186         }
187
188       if (!strncmp (line,"movc\ta",6) ||
189           !strncmp (line,"movx\ta",6))
190         {
191           REGS8051_UNSET (A_IDX);
192           return;
193         }
194
195       /* move direct to symbol, do not care */
196       if (!strncmp (line,"mov\t_",5) ||
197           !strncmp (line,"mov\t(_",6))
198         return;
199
200       /* check literal mov to register */
201       if (!strncmp (line,"mov\tr",5))
202         {
203           char *s;
204           int value;
205           int regNum;
206
207           regNum = strtol (line+5, &s, 16);
208           if (s == line+6)
209             {
210               value = strtol (line+8, &s, 16);
211               if ((s != line+8) && !strncmp (line+6,",#0x",4))
212                 REGS8051_SET (Rx_NUM_TO_IDX(regNum), value);
213               else
214                 REGS8051_UNSET (Rx_NUM_TO_IDX(regNum));
215               return;
216             }
217         }
218
219       /* mov to psw can change register bank */
220       if (!strncmp (line,"mov\tpsw,",8))
221         {
222           invalidateAllRx();
223           return;
224         }
225
226       /* no tracking of these, so we do not care */
227       if (!strncmp (line,"mov\tdptr,#",10) ||
228           !strncmp (line,"mov\tdpl,",8) ||
229           !strncmp (line,"mov\tdph,",8) ||
230           !strncmp (line,"mov\tsp,",7) ||
231           !strncmp (line,"mov\tb,",6))
232         return;
233
234       /* mov to xdata memory does not change registers */
235       if (!strncmp (line,"movx\t@",6))
236         return;
237
238       if (!strncmp (line,"mov\t@",5))
239         {
240           invalidateAllRx();
241           return;
242         }
243     }
244
245   /* no tracking of SP */
246   if (!strncmp (line,"push",4))
247     return;
248
249   if (!strncmp (line,"pop\ta",5))
250     {
251       if (!strncmp (line+4,"acc",3)){ REGS8051_UNSET (A_IDX); return; }
252       if (!strncmp (line+4,"ar2",3)){ REGS8051_UNSET (Rx_NUM_TO_IDX (2)); return; }
253       if (!strncmp (line+4,"ar3",3)){ REGS8051_UNSET (Rx_NUM_TO_IDX (3)); return; }
254       if (!strncmp (line+4,"ar4",3)){ REGS8051_UNSET (Rx_NUM_TO_IDX (4)); return; }
255       if (!strncmp (line+4,"ar5",3)){ REGS8051_UNSET (Rx_NUM_TO_IDX (5)); return; }
256       if (!strncmp (line+4,"ar6",3)){ REGS8051_UNSET (Rx_NUM_TO_IDX (6)); return; }
257       if (!strncmp (line+4,"ar7",3)){ REGS8051_UNSET (Rx_NUM_TO_IDX (7)); return; }
258       if (!strncmp (line+4,"ar0",3)){ REGS8051_UNSET (Rx_NUM_TO_IDX (0)); return; }
259       if (!strncmp (line+4,"ar1",3)){ REGS8051_UNSET (Rx_NUM_TO_IDX (1)); return; }
260     }
261
262   if (!strncmp (line,"inc",3))
263     {
264       /* no tracking of dptr, ignore */
265       if (!strcmp (line,"inc\tdptr") ||
266           !strcmp (line,"inc\tdph") ||
267           !strcmp (line,"inc\tdpl"))
268         return;
269
270       if (!strcmp (line,"inc\ta"))
271         {
272           if (regs8051[A_IDX].valueKnown)
273             REGS8051_SET (A_IDX, regs8051[A_IDX].value+1);
274           return;
275         }
276
277       if(!strncmp (line,"inc\tr",5))
278         {
279           regs *r = getReg(line+5);
280           if (r && r->valueKnown)
281             {
282               REGS8051_SET (r->rIdx, r->value+1);
283             }
284           return;
285         }
286     }
287
288   /* some bit in acc is cleared
289      MB: I'm too lazy to find out which right now */
290   if (!strncmp (line,"jbc\tacc",7))
291     {
292       REGS8051_UNSET (A_IDX);
293       return;
294     }
295
296   /* unfortunately the label typically following these
297      will cause loss of tracking */
298   if (!strncmp (line,"jc\t",3) ||
299       !strncmp (line,"jnc\t",4) ||
300       !strncmp (line,"jb\t",3) ||
301       !strncmp (line,"jnb\t",4) ||
302       !strncmp (line,"jbc\t",4))
303     return;
304
305   /* if branch not taken in "cjne r2,#0x08,somewhere" 
306      r2 is known to be 8 */
307   if (!strncmp (line,"cjne",4))
308     {
309       if(!strncmp (line,"cjne\ta,#0x",10))
310         {
311           char *s;
312           int value;
313
314           value = strtol (line+8, &s, 16);
315           if (s != line+8)
316               REGS8051_SET (A_IDX, value); /* valid hex found */
317         }
318       if(!strncmp (line,"cjne\tr",6))
319         {
320           char *s;
321           int value;
322           regs *r = getReg(line+6);
323           value = strtol (line+8, &s, 16);
324           if (r && s != line+8)
325               REGS8051_SET (r->rIdx, value); /* valid hex found */
326         }
327       return;
328     }
329
330   /* acc eventually known to be zero */
331   if (!strncmp (line,"jz\t",3))
332     return;
333
334   /* acc eventually known to be zero */
335   if (!strncmp (line,"jnz\t",4))
336     {
337       REGS8051_SET (A_IDX, 0x00); // branch not taken
338       return;
339     }
340
341   if (!strncmp (line,"djnz\tr",6))
342     {
343       char *s;
344       int regNum;
345
346       regNum = strtol (line+6, &s, 16);
347       if (s == line+7)
348         {
349           //REGS8051_UNSET (Rx_NUM_TO_IDX(regNum));
350           REGS8051_SET (Rx_NUM_TO_IDX(regNum), 0x00); // branch not taken
351           return;
352         }
353     }
354
355   /* only carry bit, so we do not care */
356   if (!strncmp (line,"setb\tc",6) ||
357       !strncmp (line,"clr\tc",5) ||
358       !strncmp (line,"cpl\tc",5))
359     return;
360
361   if (!strncmp (line,"add\ta,",6) ||
362       !strncmp (line,"addc\ta,",7)||
363       !strncmp (line,"subb\ta,",7)||
364       !strncmp (line,"xrl\ta,",6) ||
365       !strncmp (line,"orl\ta,",6) ||
366       !strncmp (line,"anl\ta,",6) ||
367       !strncmp (line,"da\ta",4)   ||
368       !strncmp (line,"rlc\ta,",6) ||
369       !strncmp (line,"rrc\ta,",6) ||
370       !strncmp (line,"setb\ta",6) ||
371       !strncmp (line,"clrb\ta,",7)||
372       !strncmp (line,"cpl\tacc",7))
373     {
374       /* could also handle f.e. "add a,Rx" if a, Rx are known or "xrl a,#0x08" */
375       REGS8051_UNSET (A_IDX);
376       return;
377     }
378
379
380   if (!strncmp (line,"dec",3))
381     {
382       /* no tracking of dptr, so we would not care */
383       if (!strcmp (line,"dec\tdph") ||
384           !strcmp (line,"dec\tdpl"))
385         return;
386
387       if (!strcmp (line,"dec\ta"))
388         {
389           if (regs8051[A_IDX].valueKnown)
390             REGS8051_SET (A_IDX, regs8051[A_IDX].value-1);
391           return;
392         }
393
394       if(!strncmp (line,"dec\tr",5))
395         {
396           regs *r = getReg(line+5);
397           if (r && r->valueKnown)
398             {
399               REGS8051_SET (r->rIdx, r->value-1);
400             }
401           return;
402         }
403     }
404
405
406   if (!strcmp (line,"clr\ta"))
407     {
408       REGS8051_SET (A_IDX, 0);
409       return;
410     }
411
412   if (!strcmp (line,"cpl\ta"))
413     {
414       if (regs8051[A_IDX].valueKnown)
415         REGS8051_SET (A_IDX, ~regs8051[A_IDX].value);
416       return;
417     }
418   if (!strcmp (line,"rl\ta"))
419     {
420       if (regs8051[A_IDX].valueKnown)
421         REGS8051_SET (A_IDX, (regs8051[A_IDX].value<<1) | 
422                              (regs8051[A_IDX].value>>7) );
423       return;
424     }
425   if (!strcmp (line,"rr\ta"))
426     {
427       if (regs8051[A_IDX].valueKnown)
428         REGS8051_SET (A_IDX, (regs8051[A_IDX].value>>1) |
429                              (regs8051[A_IDX].value<<7));
430       return;
431     }
432   if (!strcmp (line,"swap\ta"))
433     {
434       if (regs8051[A_IDX].valueKnown)
435         REGS8051_SET (A_IDX, (regs8051[A_IDX].value>>4) |
436                              (regs8051[A_IDX].value<<4));
437       return;
438     }
439
440   if (!strncmp (line,"mul",3) ||
441       !strncmp (line,"div",3)
442       )
443     {
444       REGS8051_UNSET (A_IDX);
445       REGS8051_UNSET (B_IDX);
446       return;
447     }
448
449   /* assuming these library functions have no side-effects */
450   if (!strcmp (line,"lcall"))
451     {
452       if (!strcmp (line,"lcall\t__gptrput"))
453         {
454           /* invalidate R0..R7 because they might have been changed */
455           /* MB: too paranoid ? */
456           //invalidateAllRx();
457           return;
458         }
459       if (!strcmp (line,"lcall\t__gptrget"))
460         {
461           REGS8051_UNSET (A_IDX);
462           return;
463         }
464       if (!strcmp (line,"lcall\t__decdptr"))
465         {
466           return;
467         }
468      }
469
470   if (!strncmp (line,"xch\ta,r",7))
471     {
472       /* handle xch acc with Rn */
473       regs *r = getReg(line+7);
474       if (r)
475         {
476           unsigned swap;
477           swap = r->valueKnown;
478           r->valueKnown = regs8051[A_IDX].valueKnown;
479           regs8051[A_IDX].valueKnown = swap;
480
481           swap = r->value;
482           r->value = regs8051[A_IDX].value;
483           regs8051[A_IDX].value = swap;
484           return;
485         }
486     }
487
488   /* all others unrecognized, invalidate */
489   invalidateAll();
490 }
491
492
493 /* expects f.e. "#0x01" and returns either "#0x01"
494    if the value is not known to be within registers
495    or "a" or "r0".."r7".
496    (mov a,r7 or add a,r7 need one byte whereas
497     mov a,#0x01 or add a,#0x01 would take two
498  */
499 char * rtrackGetLit(const char *x)
500 {
501   unsigned int i;
502
503   char *s;
504
505   if (enable != 1)
506     return (char *)x;
507
508   /* was it a numerical literal? */
509   if (*x == '#')
510     {
511       int val = strtol (x+1, &s, 16);
512       if (x+1 != s)
513         {
514           /* try to get from acc */
515           regs *r = &regs8051[A_IDX];
516           if (r->valueKnown &&
517               r->value == val)
518             {
519               D(emitcode (";", "genFromRTrack 0x%02x=%s", val, r->name));
520               return r->name;
521             }
522           /* try to get from register R0..R7 */
523           for (i=0; i<8; i++)
524             {
525               regs *r = &regs8051[Rx_NUM_TO_IDX(i)];
526               if (r->valueKnown &&
527                   r->value == val)
528                 {
529                   D(emitcode (";", "genFromRTrack 0x%02x=%s", val, r->name));
530                   return r->name;
531                 }
532             }
533         }
534       else
535         {
536           /* probably a symbolic literal as in "mov r3,#(_i+1)",
537              not handled... */
538         }
539     }
540
541   return (char *)x;
542 }
543
544 /* Similar to the above function 
545    As the destination is the accumulator try harder yet and
546    try to generate the result with arithmetic operations */
547 int rtrackMoveALit (const char *x)
548 {
549
550   if (enable != 1)
551     return 0;
552
553   /* if it is a literal mov try to get it cheaper */
554   if ( *x == '#' )
555     {
556       regs *a = &regs8051[A_IDX];
557
558       char *s;
559       int val = strtol (x+1, &s, 16);
560
561       /* was it a numerical literal? */
562       if (x+1 != s)
563         {
564           /* prefer mov a,#0x00 */
565           if (val == 0 &&
566               ((a->valueKnown && a->value != 0) ||
567                !a->valueKnown))
568             {
569               /* peepholes convert to clr a */
570               /* MB: why not here ? */
571               emitcode ("mov", "a,#0x00");
572               return 1;
573             }
574
575           if (a->valueKnown)
576             {
577               /* already there? */
578               if (val == a->value)
579                 {
580                   D(emitcode (";", "genFromRTrack acc=0x%02x", a->value));
581                   return 1;
582                 }
583
584               /* can be calculated with an instruction
585                  that does not change flags from acc itself? */
586               if (val == ((a->value+1) & 0xff) )
587                 {
588                   D(emitcode (";", "genFromRTrack 0x%02x=0x%02x+1", val, a->value));
589                   emitcode ("inc", "a");
590                   return 1;
591                 }
592               if (val == ((a->value-1) & 0xff) )
593                 {
594                   D(emitcode (";", "genFromRTrack 0x%02x=0x%02x-1", val, a->value));
595                   emitcode ("dec", "a");
596                   return 1;
597                 }
598               if (val == ((~a->value) & 0xff) )
599                 {
600                   D(emitcode (";", "genFromRTrack 0x%02x=~0x%02x", val, a->value));
601                   emitcode ("cpl", "a");
602                   return 1;
603                 }
604               if (val == (((a->value>>1) |
605                            (a->value<<7)) & 0xff))
606                 {
607                   D(emitcode (";", "genFromRTrack 0x%02x=rr(0x%02x)", val, a->value));
608                   emitcode ("rr", "a");
609                   return 1;
610                 }
611               if (val == (((a->value<<1) |
612                            (a->value>>7)) & 0xff ))
613                 {
614                   D(emitcode (";", "genFromRTrack 0x%02x=rl(0x%02x)", val, a->value));
615                   emitcode ("rl", "a");
616                   return 1;
617                 }
618               if (val == ( ((a->value & 0x0f)<<4) |
619                            ((a->value & 0xf0)>>4) ))
620                 {
621                   D(emitcode (";", "genFromRTrack 0x%02x=swap(0x%02x)", val, a->value));
622                   emitcode ("swap", "a");
623                   return 1;
624                 }
625               /* Decimal Adjust Accumulator (da a) changes flags so not used */
626             }
627
628
629           {
630             unsigned int i;
631             char *ptr= rtrackGetLit(x);
632
633             if (x != ptr)
634               {
635                 /* could get from register, fine */
636                 emitcode ("mov", "a,%s", ptr);
637                 return 1;
638               }
639
640             /* not yet giving up - try to calculate from register R0..R7 */
641             for (i=0; i<8; i++)
642               {
643                 regs *r = &regs8051[Rx_NUM_TO_IDX(i)];
644
645                 if (a->valueKnown && r->valueKnown)
646                   {
647                     /* calculate with a single byte instruction from R0..R7? */
648                     if (val == (a->value | r->value))
649                       {
650                         D(emitcode (";", "genFromRTrack 0x%02x=0x%02x|0x%02x",
651                                     val, a->value, r->value));
652                         emitcode ("orl", "a,%s",r->name);
653                         return 1;
654                       }
655                     if (val == (a->value & r->value))
656                       {
657                         D(emitcode (";", "genFromRTrack 0x%02x=0x%02x&0x%02x",
658                                     val, a->value, r->value));
659                         emitcode ("anl", "a,%s", r->name);
660                         return 1;
661                       }
662                     if (val == (a->value ^ r->value))
663                       {
664                         D(emitcode (";", "genFromRTrack 0x%02x=0x%02x^0x%02x",
665                                     val, a->value, r->value));
666                         emitcode ("xrl", "a,%s", r->name);
667                         return 1;
668                       }
669                     /* changes flags (does that matter?)
670                     if (val == (a->value + r->value))
671                       {
672                         D(emitcode (";", "genFromRTrack 0x%02x=0x%02x+%0x02x",
673                                     val, a->value, r->value));
674                         emitcode ("add", "a,%s",r->name);
675                         return 1;
676                       }
677                     so not used */
678                   }
679               }
680           }
681       }
682     }
683
684   return 0;
685 }
686