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