1 /*-------------------------------------------------------------------------
2 rtrack.c - tracking content of registers on an mcs51
4 Copyright 2007 Frieder Ferlemann (Frieder Ferlemann AT web.de)
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.
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.
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 -------------------------------------------------------------------------*/
21 /*-------------------------------------------------------------------------
23 - passes regression test suite, still bugs are likely
24 - only active if environment variable SDCC_RTRACK is set
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 -------------------------------------------------------------------------*/
40 #include "SDCCglobl.h"
49 #define D(x) do if (options.verboseAsm) {x;} while(0)
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);) \
58 #define REGS8051_UNSET(idx) do{ \
59 regs8051[idx].valueKnown = 0; \
60 DEBUG(printf("%s:*\n",regs8051[idx].name);) \
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))
67 /* move this (or rtrackGetLit() and rtrackMoveALit()
68 elsewhere? stealing emitcode from gen.c */
69 void emitcode (const char *inst, const char *fmt,...);
72 static int enable = -1;
80 for (i=0; i<END_IDX; i++)
82 if (regs8051[i].valueKnown)
86 DEBUG(printf("know:");)
88 DEBUG(printf(" %s:0x%02x",regs8051[i].name,regs8051[i].value);)
100 static void invalidateAllRx()
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;
114 static void invalidateAll()
116 DEBUG(printf("All:* ");)
118 regs8051[DPL_IDX].valueKnown = 0;
119 regs8051[DPH_IDX].valueKnown = 0;
120 regs8051[B_IDX].valueKnown = 0;
121 regs8051[A_IDX].valueKnown = 0;
124 static regs * getReg(const char *str)
129 regNum = strtol (str, &s, 16);
132 return ®s8051[Rx_NUM_TO_IDX(regNum)];
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)
143 enable = (NULL != getenv("SDCC_RTRACK"));
146 *line == ';' || /* comment */
147 (NULL != strstr( line, "==."))) /* dirty check for _G.debugLine */
148 return; /* nothing to do */
150 DEBUG(printf("%s\n",line);)
152 if (!strncmp (line,"mov",3))
154 /* check literal mov to accumulator */
155 if(!strncmp (line,"mov\ta,#0x",9))
160 value = strtol (line+7, &s, 16);
162 REGS8051_SET (A_IDX, value); /* valid hex found */
164 REGS8051_UNSET (A_IDX); /* probably a symbol (not handled) */
169 if (!strncmp (line,"mov\ta,r",7))
171 /* handle mov from Rx if Rx is known */
172 regs *r = getReg(line+7);
173 if (r && r->valueKnown)
175 REGS8051_SET (A_IDX, r->value);
178 REGS8051_UNSET (A_IDX);
182 if (!strncmp (line,"mov\ta",5))
184 REGS8051_UNSET (A_IDX);
188 if (!strncmp (line,"movc\ta",6) ||
189 !strncmp (line,"movx\ta",6))
191 REGS8051_UNSET (A_IDX);
195 /* move direct to symbol, do not care */
196 if (!strncmp (line,"mov\t_",5) ||
197 !strncmp (line,"mov\t(_",6))
200 /* check literal mov to register */
201 if (!strncmp (line,"mov\tr",5))
207 regNum = strtol (line+5, &s, 16);
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);
214 REGS8051_UNSET (Rx_NUM_TO_IDX(regNum));
219 /* mov to psw can change register bank */
220 if (!strncmp (line,"mov\tpsw,",8))
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))
234 /* mov to xdata memory does not change registers */
235 if (!strncmp (line,"movx\t@",6))
238 if (!strncmp (line,"mov\t@",5))
245 /* no tracking of SP */
246 if (!strncmp (line,"push",4))
249 if (!strncmp (line,"pop\ta",5))
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; }
262 if (!strncmp (line,"inc",3))
264 /* no tracking of dptr, ignore */
265 if (!strcmp (line,"inc\tdptr") ||
266 !strcmp (line,"inc\tdph") ||
267 !strcmp (line,"inc\tdpl"))
270 if (!strcmp (line,"inc\ta"))
272 if (regs8051[A_IDX].valueKnown)
273 REGS8051_SET (A_IDX, regs8051[A_IDX].value+1);
277 if(!strncmp (line,"inc\tr",5))
279 regs *r = getReg(line+5);
280 if (r && r->valueKnown)
282 REGS8051_SET (r->rIdx, r->value+1);
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))
292 REGS8051_UNSET (A_IDX);
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))
305 /* if branch not taken in "cjne r2,#0x08,somewhere"
306 r2 is known to be 8 */
307 if (!strncmp (line,"cjne",4))
309 if(!strncmp (line,"cjne\ta,#0x",10))
314 value = strtol (line+8, &s, 16);
316 REGS8051_SET (A_IDX, value); /* valid hex found */
318 if(!strncmp (line,"cjne\tr",6))
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 */
330 /* acc eventually known to be zero */
331 if (!strncmp (line,"jz\t",3))
334 /* acc eventually known to be zero */
335 if (!strncmp (line,"jnz\t",4))
337 REGS8051_SET (A_IDX, 0x00); // branch not taken
341 if (!strncmp (line,"djnz\tr",6))
346 regNum = strtol (line+6, &s, 16);
349 //REGS8051_UNSET (Rx_NUM_TO_IDX(regNum));
350 REGS8051_SET (Rx_NUM_TO_IDX(regNum), 0x00); // branch not taken
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))
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))
374 /* could also handle f.e. "add a,Rx" if a, Rx are known or "xrl a,#0x08" */
375 REGS8051_UNSET (A_IDX);
380 if (!strncmp (line,"dec",3))
382 /* no tracking of dptr, so we would not care */
383 if (!strcmp (line,"dec\tdph") ||
384 !strcmp (line,"dec\tdpl"))
387 if (!strcmp (line,"dec\ta"))
389 if (regs8051[A_IDX].valueKnown)
390 REGS8051_SET (A_IDX, regs8051[A_IDX].value-1);
394 if(!strncmp (line,"dec\tr",5))
396 regs *r = getReg(line+5);
397 if (r && r->valueKnown)
399 REGS8051_SET (r->rIdx, r->value-1);
406 if (!strcmp (line,"clr\ta"))
408 REGS8051_SET (A_IDX, 0);
412 if (!strcmp (line,"cpl\ta"))
414 if (regs8051[A_IDX].valueKnown)
415 REGS8051_SET (A_IDX, ~regs8051[A_IDX].value);
418 if (!strcmp (line,"rl\ta"))
420 if (regs8051[A_IDX].valueKnown)
421 REGS8051_SET (A_IDX, (regs8051[A_IDX].value<<1) |
422 (regs8051[A_IDX].value>>7) );
425 if (!strcmp (line,"rr\ta"))
427 if (regs8051[A_IDX].valueKnown)
428 REGS8051_SET (A_IDX, (regs8051[A_IDX].value>>1) |
429 (regs8051[A_IDX].value<<7));
432 if (!strcmp (line,"swap\ta"))
434 if (regs8051[A_IDX].valueKnown)
435 REGS8051_SET (A_IDX, (regs8051[A_IDX].value>>4) |
436 (regs8051[A_IDX].value<<4));
440 if (!strncmp (line,"mul",3) ||
441 !strncmp (line,"div",3)
444 REGS8051_UNSET (A_IDX);
445 REGS8051_UNSET (B_IDX);
449 /* assuming these library functions have no side-effects */
450 if (!strcmp (line,"lcall"))
452 if (!strcmp (line,"lcall\t__gptrput"))
454 /* invalidate R0..R7 because they might have been changed */
455 /* MB: too paranoid ? */
459 if (!strcmp (line,"lcall\t__gptrget"))
461 REGS8051_UNSET (A_IDX);
464 if (!strcmp (line,"lcall\t__decdptr"))
470 if (!strncmp (line,"xch\ta,r",7))
472 /* handle xch acc with Rn */
473 regs *r = getReg(line+7);
477 swap = r->valueKnown;
478 r->valueKnown = regs8051[A_IDX].valueKnown;
479 regs8051[A_IDX].valueKnown = swap;
482 r->value = regs8051[A_IDX].value;
483 regs8051[A_IDX].value = swap;
488 /* all others unrecognized, invalidate */
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
499 char * rtrackGetLit(const char *x)
508 /* was it a numerical literal? */
511 int val = strtol (x+1, &s, 16);
514 /* try to get from acc */
515 regs *r = ®s8051[A_IDX];
519 D(emitcode (";", "genFromRTrack 0x%02x=%s", val, r->name));
522 /* try to get from register R0..R7 */
525 regs *r = ®s8051[Rx_NUM_TO_IDX(i)];
529 D(emitcode (";", "genFromRTrack 0x%02x=%s", val, r->name));
536 /* probably a symbolic literal as in "mov r3,#(_i+1)",
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)
553 /* if it is a literal mov try to get it cheaper */
556 regs *a = ®s8051[A_IDX];
559 int val = strtol (x+1, &s, 16);
561 /* was it a numerical literal? */
564 /* prefer mov a,#0x00 */
566 ((a->valueKnown && a->value != 0) ||
569 /* peepholes convert to clr a */
570 /* MB: why not here ? */
571 emitcode ("mov", "a,#0x00");
580 D(emitcode (";", "genFromRTrack acc=0x%02x", a->value));
584 /* can be calculated with an instruction
585 that does not change flags from acc itself? */
586 if (val == ((a->value+1) & 0xff) )
588 D(emitcode (";", "genFromRTrack 0x%02x=0x%02x+1", val, a->value));
589 emitcode ("inc", "a");
592 if (val == ((a->value-1) & 0xff) )
594 D(emitcode (";", "genFromRTrack 0x%02x=0x%02x-1", val, a->value));
595 emitcode ("dec", "a");
598 if (val == ((~a->value) & 0xff) )
600 D(emitcode (";", "genFromRTrack 0x%02x=~0x%02x", val, a->value));
601 emitcode ("cpl", "a");
604 if (val == (((a->value>>1) |
605 (a->value<<7)) & 0xff))
607 D(emitcode (";", "genFromRTrack 0x%02x=rr(0x%02x)", val, a->value));
608 emitcode ("rr", "a");
611 if (val == (((a->value<<1) |
612 (a->value>>7)) & 0xff ))
614 D(emitcode (";", "genFromRTrack 0x%02x=rl(0x%02x)", val, a->value));
615 emitcode ("rl", "a");
618 if (val == ( ((a->value & 0x0f)<<4) |
619 ((a->value & 0xf0)>>4) ))
621 D(emitcode (";", "genFromRTrack 0x%02x=swap(0x%02x)", val, a->value));
622 emitcode ("swap", "a");
625 /* Decimal Adjust Accumulator (da a) changes flags so not used */
631 char *ptr= rtrackGetLit(x);
635 /* could get from register, fine */
636 emitcode ("mov", "a,%s", ptr);
640 /* not yet giving up - try to calculate from register R0..R7 */
643 regs *r = ®s8051[Rx_NUM_TO_IDX(i)];
645 if (a->valueKnown && r->valueKnown)
647 /* calculate with a single byte instruction from R0..R7? */
648 if (val == (a->value | r->value))
650 D(emitcode (";", "genFromRTrack 0x%02x=0x%02x|0x%02x",
651 val, a->value, r->value));
652 emitcode ("orl", "a,%s",r->name);
655 if (val == (a->value & r->value))
657 D(emitcode (";", "genFromRTrack 0x%02x=0x%02x&0x%02x",
658 val, a->value, r->value));
659 emitcode ("anl", "a,%s", r->name);
662 if (val == (a->value ^ r->value))
664 D(emitcode (";", "genFromRTrack 0x%02x=0x%02x^0x%02x",
665 val, a->value, r->value));
666 emitcode ("xrl", "a,%s", r->name);
669 /* changes flags (does that matter?)
670 if (val == (a->value + r->value))
672 D(emitcode (";", "genFromRTrack 0x%02x=0x%02x+%0x02x",
673 val, a->value, r->value));
674 emitcode ("add", "a,%s",r->name);