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 -------------------------------------------------------------------------*/
38 #include "SDCCglobl.h"
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);) \
56 #define REGS8051_UNSET(idx) do{ \
57 regs8051[idx].valueKnown = 0; \
58 DEBUG(printf("%s:*\n",regs8051[idx].name);) \
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))
65 /* move this (or rtrackGetLit() and rtrackMoveALit()
66 elsewhere? stealing emitcode from gen.c */
67 void emitcode (const char *inst, const char *fmt,...);
70 static int enable = -1;
78 for (i=0; i<END_IDX; i++)
80 if (regs8051[i].valueKnown)
84 DEBUG(printf("know:");)
86 DEBUG(printf(" %s:0x%02x",regs8051[i].name,regs8051[i].value);)
98 static void invalidateAllRx()
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;
112 static void invalidateAll()
114 DEBUG(printf("All:* ");)
116 regs8051[DPL_IDX].valueKnown = 0;
117 regs8051[DPH_IDX].valueKnown = 0;
118 regs8051[B_IDX].valueKnown = 0;
119 regs8051[A_IDX].valueKnown = 0;
122 static regs * getReg(const char *str)
127 regNum = strtol (str, &s, 16);
130 return ®s8051[Rx_NUM_TO_IDX(regNum)];
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)
141 enable = (NULL != getenv("SDCC_RTRACK"));
144 *line == ';' || /* comment */
145 (NULL != strstr( line, "==."))) /* dirty check for _G.debugLine */
146 return; /* nothing to do */
148 DEBUG(printf("%s\n",line);)
150 if (!strncmp (line,"mov",3))
152 /* check literal mov to accumulator */
153 if(!strncmp (line,"mov\ta,#0x",9))
158 value = strtol (line+7, &s, 16);
160 REGS8051_SET (A_IDX, value); /* valid hex found */
162 REGS8051_UNSET (A_IDX); /* probably a symbol (not handled) */
167 if (!strncmp (line,"mov\ta,r",7))
169 /* handle mov from Rx if Rx is known */
170 regs *r = getReg(line+7);
171 if (r && r->valueKnown)
173 REGS8051_SET (A_IDX, r->value);
176 REGS8051_UNSET (A_IDX);
180 if (!strncmp (line,"mov\ta",5))
182 REGS8051_UNSET (A_IDX);
186 if (!strncmp (line,"movc\ta",6) ||
187 !strncmp (line,"movx\ta",6))
189 REGS8051_UNSET (A_IDX);
193 /* move direct to symbol, do not care */
194 if (!strncmp (line,"mov\t_",5) ||
195 !strncmp (line,"mov\t(_",6))
198 /* check literal mov to register */
199 if (!strncmp (line,"mov\tr",5))
205 regNum = strtol (line+5, &s, 16);
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);
212 REGS8051_UNSET (Rx_NUM_TO_IDX(regNum));
217 /* mov to psw can change register bank */
218 if (!strncmp (line,"mov\tpsw,",8))
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))
232 /* mov to xdata memory does not change registers */
233 if (!strncmp (line,"movx\t@",6))
236 if (!strncmp (line,"mov\t@",5))
243 /* no tracking of SP */
244 if (!strncmp (line,"push",4))
247 if (!strncmp (line,"pop\ta",5))
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; }
260 if (!strncmp (line,"inc",3))
262 /* no tracking of dptr, ignore */
263 if (!strcmp (line,"inc\tdptr") ||
264 !strcmp (line,"inc\tdph") ||
265 !strcmp (line,"inc\tdpl"))
268 if (!strcmp (line,"inc\ta"))
270 if (regs8051[A_IDX].valueKnown)
271 REGS8051_SET (A_IDX, regs8051[A_IDX].value+1);
275 if(!strncmp (line,"inc\tr",5))
277 regs *r = getReg(line+5);
278 if (r && r->valueKnown)
280 REGS8051_SET (r->rIdx, r->value+1);
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))
290 REGS8051_UNSET (A_IDX);
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))
303 /* if branch not taken in "cjne r2,#0x08,somewhere"
304 r2 is known to be 8 */
305 if (!strncmp (line,"cjne",4))
307 if(!strncmp (line,"cjne\ta,#0x",10))
312 value = strtol (line+8, &s, 16);
314 REGS8051_SET (A_IDX, value); /* valid hex found */
316 if(!strncmp (line,"cjne\tr",6))
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 */
328 /* acc eventually known to be zero */
329 if (!strncmp (line,"jz\t",3))
332 /* acc eventually known to be zero */
333 if (!strncmp (line,"jnz\t",4))
335 REGS8051_SET (A_IDX, 0x00); // branch not taken
339 if (!strncmp (line,"djnz\tr",6))
344 regNum = strtol (line+6, &s, 16);
347 //REGS8051_UNSET (Rx_NUM_TO_IDX(regNum));
348 REGS8051_SET (Rx_NUM_TO_IDX(regNum), 0x00); // branch not taken
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))
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))
372 /* could also handle f.e. "add a,Rx" if a, Rx are known or "xrl a,#0x08" */
373 REGS8051_UNSET (A_IDX);
378 if (!strncmp (line,"dec",3))
380 /* no tracking of dptr, so we would not care */
381 if (!strcmp (line,"dec\tdph") ||
382 !strcmp (line,"dec\tdpl"))
385 if (!strcmp (line,"dec\ta"))
387 if (regs8051[A_IDX].valueKnown)
388 REGS8051_SET (A_IDX, regs8051[A_IDX].value-1);
392 if(!strncmp (line,"dec\tr",5))
394 regs *r = getReg(line+5);
395 if (r && r->valueKnown)
397 REGS8051_SET (r->rIdx, r->value-1);
404 if (!strcmp (line,"clr\ta"))
406 REGS8051_SET (A_IDX, 0);
410 if (!strcmp (line,"cpl\ta"))
412 if (regs8051[A_IDX].valueKnown)
413 REGS8051_SET (A_IDX, ~regs8051[A_IDX].value);
416 if (!strcmp (line,"rl\ta"))
418 if (regs8051[A_IDX].valueKnown)
419 REGS8051_SET (A_IDX, (regs8051[A_IDX].value<<1) |
420 (regs8051[A_IDX].value>>7) );
423 if (!strcmp (line,"rr\ta"))
425 if (regs8051[A_IDX].valueKnown)
426 REGS8051_SET (A_IDX, (regs8051[A_IDX].value>>1) |
427 (regs8051[A_IDX].value<<7));
430 if (!strcmp (line,"swap\ta"))
432 if (regs8051[A_IDX].valueKnown)
433 REGS8051_SET (A_IDX, (regs8051[A_IDX].value>>4) |
434 (regs8051[A_IDX].value<<4));
438 if (!strncmp (line,"mul",3) ||
439 !strncmp (line,"div",3)
442 REGS8051_UNSET (A_IDX);
443 REGS8051_UNSET (B_IDX);
447 /* assuming these library functions have no side-effects */
448 if (!strcmp (line,"lcall"))
450 if (!strcmp (line,"lcall\t__gptrput"))
452 /* invalidate R0..R7 because they might have been changed */
453 /* MB: too paranoid ? */
457 if (!strcmp (line,"lcall\t__gptrget"))
459 REGS8051_UNSET (A_IDX);
462 if (!strcmp (line,"lcall\t__decdptr"))
468 if (!strncmp (line,"xch\ta,r",7))
470 /* handle xch acc with Rn */
471 regs *r = getReg(line+7);
475 swap = r->valueKnown;
476 r->valueKnown = regs8051[A_IDX].valueKnown;
477 regs8051[A_IDX].valueKnown = swap;
480 r->value = regs8051[A_IDX].value;
481 regs8051[A_IDX].value = swap;
486 /* all others unrecognized, invalidate */
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
497 char * rtrackGetLit(const char *x)
506 /* was it a numerical literal? */
509 int val = strtol (x+1, &s, 16);
512 /* try to get from acc */
513 regs *r = ®s8051[A_IDX];
517 D(emitcode (";", "genFromRTrack 0x%02x=%s", val, r->name));
520 /* try to get from register R0..R7 */
523 regs *r = ®s8051[Rx_NUM_TO_IDX(i)];
527 D(emitcode (";", "genFromRTrack 0x%02x=%s", val, r->name));
534 /* probably a symbolic literal as in "mov r3,#(_i+1)",
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)
551 /* if it is a literal mov try to get it cheaper */
554 regs *a = ®s8051[A_IDX];
557 int val = strtol (x+1, &s, 16);
559 /* was it a numerical literal? */
562 /* prefer mov a,#0x00 */
564 ((a->valueKnown && a->value != 0) ||
567 /* peepholes convert to clr a */
568 /* MB: why not here ? */
569 emitcode ("mov", "a,#0x00");
578 D(emitcode (";", "genFromRTrack acc=0x%02x", a->value));
582 /* can be calculated with an instruction
583 that does not change flags from acc itself? */
584 if (val == ((a->value+1) & 0xff) )
586 D(emitcode (";", "genFromRTrack 0x%02x=0x%02x+1", val, a->value));
587 emitcode ("inc", "a");
590 if (val == ((a->value-1) & 0xff) )
592 D(emitcode (";", "genFromRTrack 0x%02x=0x%02x-1", val, a->value));
593 emitcode ("dec", "a");
596 if (val == ((~a->value) & 0xff) )
598 D(emitcode (";", "genFromRTrack 0x%02x=~0x%02x", val, a->value));
599 emitcode ("cpl", "a");
602 if (val == (((a->value>>1) |
603 (a->value<<7)) & 0xff))
605 D(emitcode (";", "genFromRTrack 0x%02x=rr(0x%02x)", val, a->value));
606 emitcode ("rr", "a");
609 if (val == (((a->value<<1) |
610 (a->value>>7)) & 0xff ))
612 D(emitcode (";", "genFromRTrack 0x%02x=rl(0x%02x)", val, a->value));
613 emitcode ("rl", "a");
616 if (val == ( ((a->value & 0x0f)<<4) |
617 ((a->value & 0xf0)>>4) ))
619 D(emitcode (";", "genFromRTrack 0x%02x=swap(0x%02x)", val, a->value));
620 emitcode ("swap", "a");
623 /* Decimal Adjust Accumulator (da a) changes flags so not used */
629 char *ptr= rtrackGetLit(x);
633 /* could get from register, fine */
634 emitcode ("mov", "a,%s", ptr);
638 /* not yet giving up - try to calculate from register R0..R7 */
641 regs *r = ®s8051[Rx_NUM_TO_IDX(i)];
643 if (a->valueKnown && r->valueKnown)
645 /* calculate with a single byte instruction from R0..R7? */
646 if (val == (a->value | r->value))
648 D(emitcode (";", "genFromRTrack 0x%02x=0x%02x|0x%02x",
649 val, a->value, r->value));
650 emitcode ("orl", "a,%s",r->name);
653 if (val == (a->value & r->value))
655 D(emitcode (";", "genFromRTrack 0x%02x=0x%02x&0x%02x",
656 val, a->value, r->value));
657 emitcode ("anl", "a,%s", r->name);
660 if (val == (a->value ^ r->value))
662 D(emitcode (";", "genFromRTrack 0x%02x=0x%02x^0x%02x",
663 val, a->value, r->value));
664 emitcode ("xrl", "a,%s", r->name);
667 /* changes flags (does that matter?)
668 if (val == (a->value + r->value))
670 D(emitcode (";", "genFromRTrack 0x%02x=0x%02x+%0x02x",
671 val, a->value, r->value));
672 emitcode ("add", "a,%s",r->name);