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