1 /*-------------------------------------------------------------------------
\r
2 rtrack.c - tracking content of registers on an mcs51
\r
4 Copyright 2007 Frieder Ferlemann (Frieder Ferlemann AT web.de)
\r
6 This program is free software; you can redistribute it and/or modify
\r
7 it under the terms of the GNU General Public License as published by
\r
8 the Free Software Foundation; either version 2 of the License, or
\r
9 (at your option) any later version.
\r
11 This program is distributed in the hope that it will be useful,
\r
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 GNU General Public License for more details.
\r
16 You should have received a copy of the GNU General Public License
\r
17 along with this program; if not, write to the Free Software
\r
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\r
19 -------------------------------------------------------------------------*/
\r
21 /*-------------------------------------------------------------------------
\r
23 - passes regression test suite, still bugs are likely
\r
24 - only active if environment variable SDCC_RTRACK is set
\r
26 Missed opportunities:
\r
27 - does not track symbols as in "mov a,#_my_int" or "mov a,#(_my_int+1)"
\r
28 - only used with moves to acc so chances to use: "inc dptr",
\r
29 "inc r2", "add a,r2" or "mov r2,a" would not be detected)
\r
30 - a label causes loss of tracking (no handling of information of blocks
\r
31 known to follow/preceed the current block)
\r
32 - not used in aopGet or genRet
\r
33 -------------------------------------------------------------------------*/
\r
38 #include "SDCCglobl.h"
\r
45 //#define DEBUG(x) x
\r
49 #define REGS8051_SET(idx,val) do{ \
\r
50 regs8051[idx].value = (val) & 0xff; \
\r
51 regs8051[idx].valueKnown = 1; \
\r
52 DEBUG(printf("%s:0x%02x\n",regs8051[idx].name, \
\r
53 regs8051[idx].value);) \
\r
56 #define REGS8051_UNSET(idx) do{ \
\r
57 regs8051[idx].valueKnown = 0; \
\r
58 DEBUG(printf("%s:*\n",regs8051[idx].name);) \
\r
61 /* r0..r7 are not in numerical order in struct regs: r2..r7,r0,r1 */
\r
62 #define Rx_NUM_TO_IDX(num) (R2_IDX+((num-2)&0x07))
\r
65 /* move this (or rtrackGetLit() and rtrackMoveALit()
\r
66 elsewhere? stealing emitcode from gen.c */
\r
67 void emitcode (const char *inst, const char *fmt,...);
\r
70 static int enable = -1;
\r
73 static void dumpAll()
\r
78 for (i=0; i<END_IDX; i++)
\r
80 if (regs8051[i].valueKnown)
\r
84 DEBUG(printf("know:");)
\r
86 DEBUG(printf(" %s:0x%02x",regs8051[i].name,regs8051[i].value);)
\r
92 DEBUG(printf("\n");)
\r
98 static void invalidateAllRx()
\r
100 //DEBUG(dumpAll();)
\r
101 DEBUG(printf("R0..7:*\n");)
\r
102 regs8051[R2_IDX].valueKnown = 0;
\r
103 regs8051[R3_IDX].valueKnown = 0;
\r
104 regs8051[R4_IDX].valueKnown = 0;
\r
105 regs8051[R5_IDX].valueKnown = 0;
\r
106 regs8051[R6_IDX].valueKnown = 0;
\r
107 regs8051[R7_IDX].valueKnown = 0;
\r
108 regs8051[R0_IDX].valueKnown = 0;
\r
109 regs8051[R1_IDX].valueKnown = 0;
\r
112 static void invalidateAll()
\r
114 DEBUG(printf("All:* ");)
\r
116 regs8051[DPL_IDX].valueKnown = 0;
\r
117 regs8051[DPH_IDX].valueKnown = 0;
\r
118 regs8051[B_IDX].valueKnown = 0;
\r
119 regs8051[A_IDX].valueKnown = 0;
\r
122 static regs * getReg(const char *str)
\r
127 regNum = strtol (str, &s, 16);
\r
130 return ®s8051[Rx_NUM_TO_IDX(regNum)];
\r
135 /* tracking values within registers by looking
\r
136 at the line passed to the assembler.
\r
137 Tries to keep regs8051[] up to date */
\r
138 void rtrackUpdate (const char *line)
\r
141 enable = (NULL != getenv("SDCC_RTRACK"));
\r
144 *line == ';' || /* comment */
\r
145 (NULL != strstr( line, "==."))) /* dirty check for _G.debugLine */
\r
146 return; /* nothing to do */
\r
148 DEBUG(printf("%s\n",line);)
\r
150 if (!strncmp (line,"mov",3))
\r
152 /* check literal mov to accumulator */
\r
153 if(!strncmp (line,"mov\ta,#0x",9))
\r
158 value = strtol (line+7, &s, 16);
\r
160 REGS8051_SET (A_IDX, value); /* valid hex found */
\r
162 REGS8051_UNSET (A_IDX); /* probably a symbol (not handled) */
\r
167 if (!strncmp (line,"mov\ta,r",7))
\r
169 /* handle mov from Rx if Rx is known */
\r
170 regs *r = getReg(line+7);
\r
171 if (r && r->valueKnown)
\r
173 REGS8051_SET (A_IDX, r->value);
\r
176 REGS8051_UNSET (A_IDX);
\r
180 if (!strncmp (line,"mov\ta",5))
\r
182 REGS8051_UNSET (A_IDX);
\r
186 if (!strncmp (line,"movc\ta",6) ||
\r
187 !strncmp (line,"movx\ta",6))
\r
189 REGS8051_UNSET (A_IDX);
\r
193 /* move direct to symbol, do not care */
\r
194 if (!strncmp (line,"mov\t_",5) ||
\r
195 !strncmp (line,"mov\t(_",6))
\r
198 /* check literal mov to register */
\r
199 if (!strncmp (line,"mov\tr",5))
\r
205 regNum = strtol (line+5, &s, 16);
\r
208 value = strtol (line+8, &s, 16);
\r
209 if ((s != line+8) && !strncmp (line+6,",#0x",4))
\r
210 REGS8051_SET (Rx_NUM_TO_IDX(regNum), value);
\r
212 REGS8051_UNSET (Rx_NUM_TO_IDX(regNum));
\r
217 /* mov to psw can change register bank */
\r
218 if (!strncmp (line,"mov\tpsw,",8))
\r
224 /* no tracking of these, so we do not care */
\r
225 if (!strncmp (line,"mov\tdptr,#",10) ||
\r
226 !strncmp (line,"mov\tdpl,",8) ||
\r
227 !strncmp (line,"mov\tdph,",8) ||
\r
228 !strncmp (line,"mov\tsp,",7) ||
\r
229 !strncmp (line,"mov\tb,",6))
\r
232 /* mov to xdata memory does not change registers */
\r
233 if (!strncmp (line,"movx\t@",6))
\r
236 if (!strncmp (line,"mov\t@",5))
\r
243 /* no tracking of SP */
\r
244 if (!strncmp (line,"push",4))
\r
247 if (!strncmp (line,"pop\ta",5))
\r
249 if (!strncmp (line+4,"acc",3)){ REGS8051_UNSET (A_IDX); return; }
\r
250 if (!strncmp (line+4,"ar2",3)){ REGS8051_UNSET (Rx_NUM_TO_IDX (2)); return; }
\r
251 if (!strncmp (line+4,"ar3",3)){ REGS8051_UNSET (Rx_NUM_TO_IDX (3)); return; }
\r
252 if (!strncmp (line+4,"ar4",3)){ REGS8051_UNSET (Rx_NUM_TO_IDX (4)); return; }
\r
253 if (!strncmp (line+4,"ar5",3)){ REGS8051_UNSET (Rx_NUM_TO_IDX (5)); return; }
\r
254 if (!strncmp (line+4,"ar6",3)){ REGS8051_UNSET (Rx_NUM_TO_IDX (6)); return; }
\r
255 if (!strncmp (line+4,"ar7",3)){ REGS8051_UNSET (Rx_NUM_TO_IDX (7)); return; }
\r
256 if (!strncmp (line+4,"ar0",3)){ REGS8051_UNSET (Rx_NUM_TO_IDX (0)); return; }
\r
257 if (!strncmp (line+4,"ar1",3)){ REGS8051_UNSET (Rx_NUM_TO_IDX (1)); return; }
\r
260 if (!strncmp (line,"inc",3))
\r
262 /* no tracking of dptr, ignore */
\r
263 if (!strcmp (line,"inc\tdptr") ||
\r
264 !strcmp (line,"inc\tdph") ||
\r
265 !strcmp (line,"inc\tdpl"))
\r
268 if (!strcmp (line,"inc\ta"))
\r
270 if (regs8051[A_IDX].valueKnown)
\r
271 REGS8051_SET (A_IDX, regs8051[A_IDX].value+1);
\r
275 if(!strncmp (line,"inc\tr",5))
\r
277 regs *r = getReg(line+5);
\r
278 if (r && r->valueKnown)
\r
280 REGS8051_SET (r->rIdx, r->value+1);
\r
286 /* some bit in acc is cleared
\r
287 MB: I'm too lazy to find out which right now */
\r
288 if (!strncmp (line,"jbc\tacc",7))
\r
290 REGS8051_UNSET (A_IDX);
\r
294 /* unfortunately the label typically following these
\r
295 will cause loss of tracking */
\r
296 if (!strncmp (line,"jc\t",3) ||
\r
297 !strncmp (line,"jnc\t",4) ||
\r
298 !strncmp (line,"jb\t",3) ||
\r
299 !strncmp (line,"jnb\t",4) ||
\r
300 !strncmp (line,"jbc\t",4))
\r
303 /* if branch not taken in "cjne r2,#0x08,somewhere"
\r
304 r2 is known to be 8 */
\r
305 if (!strncmp (line,"cjne",4))
\r
307 if(!strncmp (line,"cjne\ta,#0x",10))
\r
312 value = strtol (line+8, &s, 16);
\r
314 REGS8051_SET (A_IDX, value); /* valid hex found */
\r
316 if(!strncmp (line,"cjne\tr",6))
\r
320 regs *r = getReg(line+6);
\r
321 value = strtol (line+8, &s, 16);
\r
322 if (r && s != line+8)
\r
323 REGS8051_SET (r->rIdx, value); /* valid hex found */
\r
328 /* acc eventually known to be zero */
\r
329 if (!strncmp (line,"jz\t",3))
\r
332 /* acc eventually known to be zero */
\r
333 if (!strncmp (line,"jnz\t",4))
\r
335 REGS8051_SET (A_IDX, 0x00); // branch not taken
\r
339 if (!strncmp (line,"djnz\tr",6))
\r
344 regNum = strtol (line+6, &s, 16);
\r
347 //REGS8051_UNSET (Rx_NUM_TO_IDX(regNum));
\r
348 REGS8051_SET (Rx_NUM_TO_IDX(regNum), 0x00); // branch not taken
\r
353 /* only carry bit, so we do not care */
\r
354 if (!strncmp (line,"setb\tc",6) ||
\r
355 !strncmp (line,"clr\tc",5) ||
\r
356 !strncmp (line,"cpl\tc",5))
\r
359 if (!strncmp (line,"add\ta,",6) ||
\r
360 !strncmp (line,"addc\ta,",7)||
\r
361 !strncmp (line,"subb\ta,",7)||
\r
362 !strncmp (line,"xrl\ta,",6) ||
\r
363 !strncmp (line,"orl\ta,",6) ||
\r
364 !strncmp (line,"anl\ta,",6) ||
\r
365 !strncmp (line,"da\ta",4) ||
\r
366 !strncmp (line,"rlc\ta,",6) ||
\r
367 !strncmp (line,"rrc\ta,",6) ||
\r
368 !strncmp (line,"setb\ta",6) ||
\r
369 !strncmp (line,"clrb\ta,",7)||
\r
370 !strncmp (line,"cpl\tacc",7))
\r
372 /* could also handle f.e. "add a,Rx" if a, Rx are known or "xrl a,#0x08" */
\r
373 REGS8051_UNSET (A_IDX);
\r
378 if (!strncmp (line,"dec",3))
\r
380 /* no tracking of dptr, so we would not care */
\r
381 if (!strcmp (line,"dec\tdph") ||
\r
382 !strcmp (line,"dec\tdpl"))
\r
385 if (!strcmp (line,"dec\ta"))
\r
387 if (regs8051[A_IDX].valueKnown)
\r
388 REGS8051_SET (A_IDX, regs8051[A_IDX].value-1);
\r
392 if(!strncmp (line,"dec\tr",5))
\r
394 regs *r = getReg(line+5);
\r
395 if (r && r->valueKnown)
\r
397 REGS8051_SET (r->rIdx, r->value-1);
\r
404 if (!strcmp (line,"clr\ta"))
\r
406 REGS8051_SET (A_IDX, 0);
\r
410 if (!strcmp (line,"cpl\ta"))
\r
412 if (regs8051[A_IDX].valueKnown)
\r
413 REGS8051_SET (A_IDX, ~regs8051[A_IDX].value);
\r
416 if (!strcmp (line,"rl\ta"))
\r
418 if (regs8051[A_IDX].valueKnown)
\r
419 REGS8051_SET (A_IDX, (regs8051[A_IDX].value<<1) |
\r
420 (regs8051[A_IDX].value>>7) );
\r
423 if (!strcmp (line,"rr\ta"))
\r
425 if (regs8051[A_IDX].valueKnown)
\r
426 REGS8051_SET (A_IDX, (regs8051[A_IDX].value>>1) |
\r
427 (regs8051[A_IDX].value<<7));
\r
430 if (!strcmp (line,"swap\ta"))
\r
432 if (regs8051[A_IDX].valueKnown)
\r
433 REGS8051_SET (A_IDX, (regs8051[A_IDX].value>>4) |
\r
434 (regs8051[A_IDX].value<<4));
\r
438 if (!strncmp (line,"mul",3) ||
\r
439 !strncmp (line,"div",3)
\r
442 REGS8051_UNSET (A_IDX);
\r
443 REGS8051_UNSET (B_IDX);
\r
447 /* assuming these library functions have no side-effects */
\r
448 if (!strcmp (line,"lcall"))
\r
450 if (!strcmp (line,"lcall\t__gptrput"))
\r
452 /* invalidate R0..R7 because they might have been changed */
\r
453 /* MB: too paranoid ? */
\r
454 //invalidateAllRx();
\r
457 if (!strcmp (line,"lcall\t__gptrget"))
\r
459 REGS8051_UNSET (A_IDX);
\r
462 if (!strcmp (line,"lcall\t__decdptr"))
\r
468 if (!strncmp (line,"xch\ta,r",7))
\r
470 /* handle xch acc with Rn */
\r
471 regs *r = getReg(line+7);
\r
475 swap = r->valueKnown;
\r
476 r->valueKnown = regs8051[A_IDX].valueKnown;
\r
477 regs8051[A_IDX].valueKnown = swap;
\r
480 r->value = regs8051[A_IDX].value;
\r
481 regs8051[A_IDX].value = swap;
\r
486 /* all others unrecognized, invalidate */
\r
491 /* expects f.e. "#0x01" and returns either "#0x01"
\r
492 if the value is not known to be within registers
\r
493 or "a" or "r0".."r7".
\r
494 (mov a,r7 or add a,r7 need one byte whereas
\r
495 mov a,#0x01 or add a,#0x01 would take two
\r
497 char * rtrackGetLit(const char *x)
\r
506 /* was it a numerical literal? */
\r
509 int val = strtol (x+1, &s, 16);
\r
512 /* try to get from acc */
\r
513 regs *r = ®s8051[A_IDX];
\r
514 if (r->valueKnown &&
\r
517 D(emitcode (";", "genFromRTrack 0x%02x=%s", val, r->name));
\r
520 /* try to get from register R0..R7 */
\r
521 for (i=0; i<8; i++)
\r
523 regs *r = ®s8051[Rx_NUM_TO_IDX(i)];
\r
524 if (r->valueKnown &&
\r
527 D(emitcode (";", "genFromRTrack 0x%02x=%s", val, r->name));
\r
534 /* probably a symbolic literal as in "mov r3,#(_i+1)",
\r
542 /* Similar to the above function
\r
543 As the destination is the accumulator try harder yet and
\r
544 try to generate the result with arithmetic operations */
\r
545 int rtrackMoveALit (const char *x)
\r
551 /* if it is a literal mov try to get it cheaper */
\r
554 regs *a = ®s8051[A_IDX];
\r
557 int val = strtol (x+1, &s, 16);
\r
559 /* was it a numerical literal? */
\r
562 /* prefer mov a,#0x00 */
\r
564 ((a->valueKnown && a->value != 0) ||
\r
567 /* peepholes convert to clr a */
\r
568 /* MB: why not here ? */
\r
569 emitcode ("mov", "a,#0x00");
\r
575 /* already there? */
\r
576 if (val == a->value)
\r
578 D(emitcode (";", "genFromRTrack acc=0x%02x", a->value));
\r
582 /* can be calculated with an instruction
\r
583 that does not change flags from acc itself? */
\r
584 if (val == ((a->value+1) & 0xff) )
\r
586 D(emitcode (";", "genFromRTrack 0x%02x=0x%02x+1", val, a->value));
\r
587 emitcode ("inc", "a");
\r
590 if (val == ((a->value-1) & 0xff) )
\r
592 D(emitcode (";", "genFromRTrack 0x%02x=0x%02x-1", val, a->value));
\r
593 emitcode ("dec", "a");
\r
596 if (val == ((~a->value) & 0xff) )
\r
598 D(emitcode (";", "genFromRTrack 0x%02x=~0x%02x", val, a->value));
\r
599 emitcode ("cpl", "a");
\r
602 if (val == (((a->value>>1) |
\r
603 (a->value<<7)) & 0xff))
\r
605 D(emitcode (";", "genFromRTrack 0x%02x=rr(0x%02x)", val, a->value));
\r
606 emitcode ("rr", "a");
\r
609 if (val == (((a->value<<1) |
\r
610 (a->value>>7)) & 0xff ))
\r
612 D(emitcode (";", "genFromRTrack 0x%02x=rl(0x%02x)", val, a->value));
\r
613 emitcode ("rl", "a");
\r
616 if (val == ( ((a->value & 0x0f)<<4) |
\r
617 ((a->value & 0xf0)>>4) ))
\r
619 D(emitcode (";", "genFromRTrack 0x%02x=swap(0x%02x)", val, a->value));
\r
620 emitcode ("swap", "a");
\r
623 /* Decimal Adjust Accumulator (da a) changes flags so not used */
\r
629 char *ptr= rtrackGetLit(x);
\r
633 /* could get from register, fine */
\r
634 emitcode ("mov", "a,%s", ptr);
\r
638 /* not yet giving up - try to calculate from register R0..R7 */
\r
639 for (i=0; i<8; i++)
\r
641 regs *r = ®s8051[Rx_NUM_TO_IDX(i)];
\r
643 if (a->valueKnown && r->valueKnown)
\r
645 /* calculate with a single byte instruction from R0..R7? */
\r
646 if (val == (a->value | r->value))
\r
648 D(emitcode (";", "genFromRTrack 0x%02x=0x%02x|0x%02x",
\r
649 val, a->value, r->value));
\r
650 emitcode ("orl", "a,%s",r->name);
\r
653 if (val == (a->value & r->value))
\r
655 D(emitcode (";", "genFromRTrack 0x%02x=0x%02x&0x%02x",
\r
656 val, a->value, r->value));
\r
657 emitcode ("anl", "a,%s", r->name);
\r
660 if (val == (a->value ^ r->value))
\r
662 D(emitcode (";", "genFromRTrack 0x%02x=0x%02x^0x%02x",
\r
663 val, a->value, r->value));
\r
664 emitcode ("xrl", "a,%s", r->name);
\r
667 /* changes flags (does that matter?)
\r
668 if (val == (a->value + r->value))
\r
670 D(emitcode (";", "genFromRTrack 0x%02x=0x%02x+%0x02x",
\r
671 val, a->value, r->value));
\r
672 emitcode ("add", "a,%s",r->name);
\r