Update svn merge history.
[sdcc.git] / sdcc / src / SDCCpeeph.c
blob6f3f5d26fdbbee9ab3b39951937d41476d3f2327
1 /*-------------------------------------------------------------------------
2 SDCCpeeph.c - The peep hole optimizer: for interpreting the
3 peep hole rules
5 Copyright (C) 1999, Sandeep Dutta . sandeep.dutta@usa.net
7 This program is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
10 later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 -------------------------------------------------------------------------*/
22 #include "common.h"
23 #include "dbuf_string.h"
25 #define ISCHARDIGIT(c) isdigit((unsigned char)c)
26 #define ISCHARSPACE(c) isspace((unsigned char)c)
27 #define ISCHARALNUM(c) isalnum((unsigned char)c)
29 #define ISINST(l, i) (!STRNCASECMP((l), (i), sizeof(i) - 1) && (!(l)[sizeof(i) - 1] || isspace((unsigned char)((l)[sizeof(i) - 1]))))
31 static peepRule *rootRules = NULL;
32 static peepRule *currRule = NULL;
34 #define HTAB_SIZE 53
36 hTab *labelHash = NULL;
38 static struct
40 allocTrace values;
41 allocTrace labels;
42 } _G;
44 static int hashSymbolName (const char *name);
45 static void buildLabelRefCountHash (lineNode * head);
46 static void bindVar (int key, char **s, hTab ** vtab);
48 static bool matchLine (char *, const char *, hTab **);
50 #define FBYNAME(x) static int x (hTab *vars, lineNode *currPl, lineNode *endPl, \
51 lineNode *head, char *cmdLine)
53 #if !OPT_DISABLE_PIC14
54 void peepRules2pCode(peepRule *);
55 #endif
57 #if !OPT_DISABLE_PIC16
58 void pic16_peepRules2pCode(peepRule *);
59 #endif
61 /*-----------------------------------------------------------------*/
62 /* getPatternVar - finds a pattern variable */
63 /*-----------------------------------------------------------------*/
65 static char*
66 getPatternVar (hTab *vars, char **cmdLine)
68 int varNumber;
69 char *digitend;
71 if (!cmdLine || !*cmdLine || !**cmdLine)
72 return NULL; /* no parameters given */
74 while (**cmdLine && ISCHARSPACE(**cmdLine))
75 (*cmdLine)++; /* skip whitespace */
77 if (**cmdLine != '%')
78 goto error;
79 (*cmdLine)++;
80 if (!ISCHARDIGIT (**cmdLine))
81 goto error;
82 varNumber = strtol (*cmdLine, &digitend, 10);
83 *cmdLine = digitend;
84 return hTabItemWithKey (vars, varNumber);
86 error:
87 fprintf (stderr,
88 "*** internal error: peephole restriction malformed: %s\n", *cmdLine);
89 return NULL;
92 /*-----------------------------------------------------------------*/
93 /* interpreteLine - interpret general ASxxxx syntax */
94 /* Returns distance */
95 /* Can recurse to interpret some simple macros */
96 /*-----------------------------------------------------------------*/
98 static int
99 interpretLine (lineNode **pl, char *buff, bool back)
101 int dist = 0;
102 if ((*pl)->line &&
103 !(*pl)->isComment &&
104 !(*pl)->isLabel &&
105 !(*pl)->isDebug)
107 // handle general ASxxxx syntax
108 // this can be a lot bigger than 4B due to macros
109 if(ISINST((*pl)->line, ".db") || ISINST((*pl)->line, ".byte") || ISINST((*pl)->line, ".fcb"))
111 int i, j;
112 for(i = 1, j = 0; (*pl)->line[j]; i += (*pl)->line[j] == ',', j++);
113 dist += i;
115 else if(ISINST((*pl)->line, ".dw") || ISINST((*pl)->line, ".word") || ISINST((*pl)->line, ".fdb"))
117 int i, j;
118 for(i = 1, j = 0; (*pl)->line[j]; i += (*pl)->line[j] == ',', j++);
119 dist += i * 2;
121 else if(ISINST((*pl)->line, ".3byte") || ISINST((*pl)->line, ".triple"))
123 int i, j;
124 for(i = 1, j = 0; (*pl)->line[j]; i += (*pl)->line[j] == ',', j++);
125 dist += i * 3;
127 else if(ISINST((*pl)->line, ".4byte") || ISINST((*pl)->line, ".quad"))
129 int i, j;
130 for(i = 1, j = 0; (*pl)->line[j]; i += (*pl)->line[j] == ',', j++);
131 dist += i * 4;
133 else if(ISINST((*pl)->line, ".str") || ISINST((*pl)->line, ".ascii") || ISINST((*pl)->line, ".fcc")
134 || ISINST((*pl)->line, ".strz") || ISINST((*pl)->line, ".asciz")
135 || ISINST((*pl)->line, ".strs") || ISINST((*pl)->line, ".ascis"))
137 const char *value;
138 // skip directive
139 for (value = (*pl)->line; *value && !isspace (*value); ++value);
140 // skip space
141 for (; *value && isspace (*value); ++value);
142 // just part of the syntax
143 if(*value == '^')
144 ++value;
145 // delimiter can be freely chosen
146 char delimiter = *(value++);
147 for (;*value && *value != delimiter; ++value, ++dist);
148 // \0 terminated string
149 if (ISINST((*pl)->line, ".strz") || ISINST((*pl)->line, ".asciz"))
150 ++dist;
151 // it doesn't end with delimiter
152 if (*value != delimiter)
154 werrorfl("delimitererror", 0, W_UNRECOGNIZED_ASM, __func__, 999, (*pl)->line);
155 return 999;
158 else if(!back && ISINST((*pl)->line, ".rept"))
160 // we can ignore the label for now
161 // rept doesn't allow duplicate labels
162 const char *op1start;
163 int times;
165 // skip directive
166 for (op1start = (*pl)->line; *op1start && !isspace (*op1start); ++op1start);
167 // skip space
168 for (; *op1start && isspace (*op1start); ++op1start);
169 times = atoi(op1start);
170 // 0 times is probably an error with atoi
171 if(times == 0)
173 werrorfl("atoierror", 0, W_UNRECOGNIZED_ASM, __func__, 999, (*pl)->line);
174 return 999;
176 (*pl) = (*pl)->next;
177 while((*pl) && !ISINST((*pl)->line, ".endm"))
179 dist += interpretLine(pl, buff, back);
180 if(!pl)
181 break;
182 (*pl) = (*pl)->next;
184 // we reached the end, not .endm
185 if(!(*pl))
186 return 999;
187 dist *= times;
189 else if(back && ISINST((*pl)->line, ".endm"))
191 // parse rept backwards
192 const char *op1start;
193 int times;
195 (*pl) = (*pl)->prev;
196 while((*pl) && !ISINST((*pl)->line, ".rept"))
198 dist += interpretLine(pl, buff, back);
199 if(!(*pl))
200 break;
201 (*pl) = (*pl)->prev;
203 // we reached the end, not .rept
204 if(!(*pl))
205 return 999;
206 // skip directive
207 for (op1start = (*pl)->line; *op1start && !isspace (*op1start); ++op1start);
208 // skip space
209 for (; *op1start && isspace (*op1start); ++op1start);
210 times = atoi(op1start);
211 // 0 times is probably an error with atoi
212 if(times == 0)
214 werrorfl("atoierror", 0, W_UNRECOGNIZED_ASM, __func__, 999, (*pl)->line);
215 return 999;
217 dist *= times;
219 else if (ISINST((*pl)->line, ".incbin"))
221 // .incbin /string/ [,offset [,count]]
222 // if count is given, we can work with it
223 const char *op1start = (*pl)->line;
224 int count;
225 // skip first comma
226 while (*op1start && *(op1start++) != ',');
227 if (!*op1start)
229 werrorfl("unsupported", 0, W_UNRECOGNIZED_ASM, __func__, 999, (*pl)->line);
230 return(999);
232 // skip first comma
233 while (*op1start && *(op1start++) != ',');
234 if (!*op1start)
236 werrorfl("unsupported", 0, W_UNRECOGNIZED_ASM, __func__, 999, (*pl)->line);
237 return(999);
239 // skip space
240 for (; *op1start && isspace (*op1start); ++op1start);
241 if (!*op1start)
243 werrorfl("unsupported", 0, W_UNRECOGNIZED_ASM, __func__, 999, (*pl)->line);
244 return(999);
246 count = atoi(op1start);
247 // probably an error with atoi
248 if (count == 0)
250 werrorfl("atoierror", 0, W_UNRECOGNIZED_ASM, __func__, 999, (*pl)->line);
251 return 999;
253 dist += count;
255 else if (ISINST((*pl)->line, ".odd") || ISINST((*pl)->line, ".even"))
257 // 0 or 1, assume worst
258 dist += 1;
260 else if(ISINST((*pl)->line, ".bndry") || ISINST((*pl)->line, ".ds") || ISINST((*pl)->line, ".rmb")
261 || ISINST((*pl)->line, ".rs") || ISINST((*pl)->line, ".blkb") || ISINST((*pl)->line, ".blkw")
262 || ISINST((*pl)->line, ".blk3") || ISINST((*pl)->line, ".blk4"))
264 const char *op1start;
265 int offset;
267 // skip directive
268 for (op1start = (*pl)->line; *op1start && !isspace (*op1start); ++op1start);
269 // skip space
270 for (; *op1start && isspace (*op1start); ++op1start);
271 offset = atoi(op1start);
272 // 0 times is probably an error with atoi
273 if (offset == 0)
275 werrorfl("atoierror", 0, W_UNRECOGNIZED_ASM, __func__, 999, (*pl)->line);
276 return 999;
278 if (ISINST((*pl)->line, ".blkw"))
279 offset *= 2;
280 if (ISINST((*pl)->line, ".blk3"))
281 offset *= 2;
282 if (ISINST((*pl)->line, ".blk4"))
283 offset *= 2;
284 // bndry: assume the worst
285 dist += offset;
287 else if ( ISINST((*pl)->line, ".iift") || ISINST((*pl)->line, ".iiff") || ISINST((*pl)->line, ".iiftf")
288 || ISINST((*pl)->line, ".iifne") || ISINST((*pl)->line, ".iifeq") || ISINST((*pl)->line, ".iifgt")
289 || ISINST((*pl)->line, ".iiflt") || ISINST((*pl)->line, ".iifge") || ISINST((*pl)->line, ".iifle")
290 || ISINST((*pl)->line, ".iifdef") || ISINST((*pl)->line, ".iifndef") || ISINST((*pl)->line, ".iifb")
291 || ISINST((*pl)->line, ".iifnb") || ISINST((*pl)->line, ".iifidn") || ISINST((*pl)->line, ".iifdif")
292 || ISINST((*pl)->line, ".iif"))
294 // the line ends with real operations and directives
295 werrorfl("unsupported", 0, W_UNRECOGNIZED_ASM, __func__, 999, (*pl)->line);
296 dist += 999;
298 else if (ISINST((*pl)->line, ".msg") || ISINST((*pl)->line, ".error") || ISINST((*pl)->line, ".assume")
299 || ISINST((*pl)->line, ".radix") || ISINST((*pl)->line, ".local") || ISINST((*pl)->line, ".globl")
300 || ISINST((*pl)->line, ".equ") || ISINST((*pl)->line, ".gblequ") || ISINST((*pl)->line, ".lclequ")
301 || ISINST((*pl)->line, ".if") || ISINST((*pl)->line, ".else") || ISINST((*pl)->line, ".endif")
302 || ISINST((*pl)->line, ".ift") || ISINST((*pl)->line, ".iff") || ISINST((*pl)->line, ".iftf")
303 || ISINST((*pl)->line, ".ifne") || ISINST((*pl)->line, ".ifeq") || ISINST((*pl)->line, ".ifgt")
304 || ISINST((*pl)->line, ".iflt") || ISINST((*pl)->line, ".ifge") || ISINST((*pl)->line, ".ifle")
305 || ISINST((*pl)->line, ".ifdef") || ISINST((*pl)->line, ".ifndef") || ISINST((*pl)->line, ".ifb")
306 || ISINST((*pl)->line, ".ifnb") || ISINST((*pl)->line, ".ifidn") || ISINST((*pl)->line, ".ifdif")
307 || ISINST((*pl)->line, ".define") || ISINST((*pl)->line, ".undefine"))
309 // ignore these, makes it longer at worst
310 dist += 0;
312 else if (ISINST((*pl)->line, ".macro") || ISINST((*pl)->line, ".irp") || ISINST((*pl)->line, ".irpc")
313 || ISINST((*pl)->line, ".include") || ISINST((*pl)->line, ".rept") || ISINST((*pl)->line, ".endm"))
315 // catch some unsupported long or complex directives
316 werrorfl("unsupported", 0, W_UNRECOGNIZED_ASM, __func__, 999, (*pl)->line);
317 dist += 999;
319 // get port specific size
320 else
322 const char *op1start;
323 // skip directive
324 for (op1start = (*pl)->line; *op1start && !isspace (*op1start); ++op1start);
325 // skip space
326 for (; *op1start && isspace (*op1start); ++op1start);
327 // catch "sym1 .equ expr" "sym1 = expr" "sym1 =: expr" etc
328 // ".equ sym1 expr" got already handled
329 if (ISINST(op1start, ".equ") || ISINST(op1start, ".gblequ") || ISINST(op1start, ".lclequ")
330 || *op1start == '=')
332 // ignored
333 dist += 0;
335 else if (port->peep.getSize)
337 dist += port->peep.getSize((*pl));
338 #if 0
339 fprintf(stderr, "Line: %s, dist: %i, total: %i\n", pl->line, port->peep.getSize(pl), dist);
340 #endif
342 else
344 // could be a macro call
345 dist += 999;
349 return dist;
352 /*-----------------------------------------------------------------*/
353 /* pcDistance - finds a label backward or forward */
354 /*-----------------------------------------------------------------*/
356 static int
357 pcDistance (lineNode *cpos, char *lbl, bool back)
359 lineNode *pl = cpos;
360 char buff[MAX_PATTERN_LEN];
361 int dist = 0;
363 SNPRINTF (buff, sizeof(buff) - 1, "%s:", lbl);
364 while (pl)
366 dist += interpretLine(&pl, buff, back);
367 // interpretLine changes pl
368 if (!pl)
369 break;
371 if (strncmp (pl->line, buff, strlen (buff)) == 0)
372 return dist;
373 if (back)
374 pl = pl->prev;
375 else
376 pl = pl->next;
378 return 0;
381 /*-----------------------------------------------------------------*/
382 /* flat24bitMode - will check to see if we are in flat24 mode */
383 /*-----------------------------------------------------------------*/
384 FBYNAME (flat24bitMode)
386 return (options.model == MODEL_FLAT24);
389 /*-----------------------------------------------------------------*/
390 /* xramMovcOption - check if using movc to read xram */
391 /*-----------------------------------------------------------------*/
392 FBYNAME (xramMovcOption)
394 return (options.xram_movc && (strcmp(port->target,"mcs51") == 0));
397 /*-----------------------------------------------------------------*/
398 /* useAcallAjmp - Enable replacement of lcall/ljmp with acall/ajmp */
399 /*-----------------------------------------------------------------*/
400 FBYNAME (useAcallAjmp)
402 return (options.acall_ajmp && (strcmp(port->target,"mcs51") == 0));
405 /*-----------------------------------------------------------------*/
406 /* labelInRange - will check to see if label is within range */
407 /*-----------------------------------------------------------------*/
408 FBYNAME (labelInRange)
410 int dist = 0;
411 char *lbl = getPatternVar (vars, &cmdLine);
413 if (!lbl)
415 fprintf (stderr,
416 "*** internal error: labelInRange peephole restriction"
417 " malformed: %s\n", cmdLine);
419 /* If no parameters given, assume that %5 pattern variable
420 has the label name for backward compatibility */
421 lbl = hTabItemWithKey (vars, 5);
424 if (!lbl)
425 return FALSE;
429 /* Don't optimize jumps in a jump table; a more generic test */
430 if (currPl->ic && currPl->ic->op == JUMPTABLE)
431 return FALSE;
433 /* if the previous two instructions are "ljmp"s then don't
434 do it since it can be part of a jump table */
435 if (currPl->prev && currPl->prev->prev &&
436 strstr (currPl->prev->line, "ljmp") &&
437 strstr (currPl->prev->prev->line, "ljmp"))
438 return FALSE;
440 /* Calculate the label distance. For mcs51 the jump can be
441 -127 to + 127 bytes, for Z80 -126 to +129 bytes.*/
442 dist = (pcDistance (currPl, lbl, TRUE) +
443 pcDistance (currPl, lbl, FALSE));
445 /* Use 125 for now. Could be made more exact using port and
446 exact jump location instead of currPl. */
447 if (!dist || dist > 127)
448 return FALSE;
450 lbl = getPatternVar (vars, &cmdLine);
452 while (lbl);
454 return TRUE;
457 /*-----------------------------------------------------------------*/
458 /* labelJTInRange - will check to see if label %5 and up are */
459 /* within range. */
460 /* Specifically meant to optimize long (3-byte) jumps to short */
461 /* (2-byte) jumps in jumptables */
462 /*-----------------------------------------------------------------*/
463 FBYNAME (labelJTInRange)
465 char *lbl;
466 int dist, count, i;
468 /* Only optimize within a jump table */
469 if (currPl->ic && currPl->ic->op != JUMPTABLE)
470 return FALSE;
472 count = elementsInSet( IC_JTLABELS (currPl->ic) );
474 /* check all labels (this is needed if the case statements are unsorted) */
475 for (i=0; i<count; i++)
477 /* assumes that the %5 pattern variable has the first ljmp label */
478 lbl = hTabItemWithKey (vars, 5+i);
479 if (!lbl)
480 return FALSE;
482 dist = (pcDistance (currPl, lbl, TRUE) +
483 pcDistance (currPl, lbl, FALSE));
485 /* three terms used to calculate allowable distance */
486 /* Could be made more exact and port-specific. */
487 if (!dist ||
488 dist > 127+ /* range of sjmp */
489 (3+3*i)+ /* offset between this jump and currPl,
490 should use pcDistance instead? */
491 (count-i-1) /* if peephole applies distance is shortened */
493 return FALSE;
495 return TRUE;
498 /*-----------------------------------------------------------------*/
499 /* optimizeReturn - is it allowed to optimize RET instructions */
500 /*-----------------------------------------------------------------*/
501 FBYNAME (optimizeReturn)
503 return (options.peepReturn >= 0);
506 /*-----------------------------------------------------------------*/
507 /* labelIsReturnOnly - Check if label is followed by ret */
508 /*-----------------------------------------------------------------*/
509 FBYNAME (labelIsReturnOnly)
511 /* assumes that %5 pattern variable has the label name */
512 const char *label, *p;
513 const lineNode *pl;
514 int len;
515 char * retInst;
517 /* Don't optimize jumps in a jump table; a more generic test */
518 if (currPl->ic && currPl->ic->op == JUMPTABLE)
519 return FALSE;
521 if (!(label = getPatternVar (vars, &cmdLine)))
523 fprintf (stderr,
524 "*** internal error: labelIsReturnOnly peephole restriction"
525 " malformed: %s\n", cmdLine);
527 /* If no parameters given, assume that %5 pattern variable
528 has the label name for backward compatibility */
529 label = hTabItemWithKey (vars, 5);
532 if (!label)
533 return FALSE;
534 len = strlen(label);
536 for(pl = currPl; pl; pl = pl->next)
538 if (pl->line && !pl->isDebug && !pl->isComment && pl->isLabel)
540 if (strncmp(pl->line, label, len) == 0)
541 break; /* Found Label */
542 if (strlen(pl->line) != 7 || !ISCHARDIGIT(*(pl->line)) ||
543 !ISCHARDIGIT(*(pl->line+1)) || !ISCHARDIGIT(*(pl->line+2)) ||
544 !ISCHARDIGIT(*(pl->line+3)) || !ISCHARDIGIT(*(pl->line+4)) ||
545 *(pl->line+5) != '$')
547 return FALSE; /* non-local label encountered */
551 if (!pl)
552 return FALSE; /* did not find the label */
553 pl = pl->next;
554 while (pl && (pl->isDebug || pl->isComment || pl->isLabel))
555 pl = pl->next;
556 if (!pl || !pl->line || pl->isDebug)
557 return FALSE; /* next line not valid */
558 for (p = pl->line; *p && ISCHARSPACE(*p); p++)
561 retInst = "ret";
562 if (TARGET_HC08_LIKE || TARGET_MOS6502_LIKE)
563 retInst = "rts";
565 if (strncmp(p, retInst,strlen(retInst)) != 0)
566 return FALSE;
568 p+=strlen(retInst);
569 while(*p && ISCHARSPACE(*p))
570 p++;
572 if(*p==0 || *p==';')
573 return TRUE;
575 return FALSE;
578 /*-----------------------------------------------------------------*/
579 /* labelIsUncondJump - Check if label %5 is followed by an */
580 /* unconditional jump and put the destination of that jump in %6 */
581 /*-----------------------------------------------------------------*/
582 FBYNAME (labelIsUncondJump)
584 /* assumes that %5 pattern variable has the label name */
585 const char *label;
586 char *p, *q;
587 const lineNode *pl;
588 bool found = FALSE;
589 int len;
590 char * jpInst = NULL;
591 char * jpInst2 = NULL;
593 label = hTabItemWithKey (vars, 5);
594 if (!label)
595 return FALSE;
596 len = strlen(label);
598 for (pl = currPl; pl; pl = pl->prev)
600 if (pl->line && !pl->isDebug && !pl->isComment && pl->isLabel)
602 if (strncmp(pl->line, label, len) == 0 && pl->line[len] == ':')
604 found = true;
605 break; /* Found Label */
607 if (strlen(pl->line) != 7 || !ISCHARDIGIT(*(pl->line)) ||
608 !ISCHARDIGIT(*(pl->line+1)) || !ISCHARDIGIT(*(pl->line+2)) ||
609 !ISCHARDIGIT(*(pl->line+3)) || !ISCHARDIGIT(*(pl->line+4)) ||
610 *(pl->line+5) != '$')
612 break; /* non-local label encountered */
617 if (!found)
619 for (pl = currPl; pl; pl = pl->next)
621 if (pl->line && !pl->isDebug && !pl->isComment && pl->isLabel)
623 if (strncmp(pl->line, label, len) == 0 && pl->line[len] == ':')
625 found = true;
626 break; /* Found Label */
628 if (strlen(pl->line) != 7 || !ISCHARDIGIT(*(pl->line)) ||
629 !ISCHARDIGIT(*(pl->line+1)) || !ISCHARDIGIT(*(pl->line+2)) ||
630 !ISCHARDIGIT(*(pl->line+3)) || !ISCHARDIGIT(*(pl->line+4)) ||
631 *(pl->line+5) != '$')
633 return FALSE; /* non-local label encountered */
639 if (!pl || !found)
640 return FALSE; /* did not find the label */
641 pl = pl->next;
642 while (pl && (pl->isDebug || pl->isComment))
643 pl = pl->next;
644 if (!pl || !pl->line)
645 return FALSE; /* next line not valid */
646 p = pl->line;
647 while (*p && ISCHARSPACE(*p))
648 p++;
650 if (TARGET_MCS51_LIKE)
652 jpInst = "ljmp";
653 jpInst2 = "sjmp";
655 else if (TARGET_HC08_LIKE || TARGET_MOS6502_LIKE)
657 jpInst = "jmp";
658 jpInst2 = "bra";
660 else if (TARGET_Z80_LIKE || TARGET_IS_F8)
662 jpInst = "jp";
663 jpInst2 = "jr";
665 else if (TARGET_IS_STM8)
667 jpInst = (options.model == MODEL_LARGE ? "jpf" : "jp");
668 jpInst2 = "jra";
670 else if (TARGET_PDK_LIKE)
672 jpInst = "goto";
674 len = strlen(jpInst);
675 if (strncmp(p, jpInst, len))
677 len = jpInst2 ? strlen(jpInst2) : 0;
678 if(!jpInst2 || strncmp(p, jpInst2, len))
679 return FALSE; /* next line is no jump */
682 p += len;
683 while (*p && ISCHARSPACE(*p))
684 p++;
686 q = p;
687 while (*q && *q!=';')
688 q++;
689 while (q>p && ISCHARSPACE(*q))
690 q--;
691 len = q-p;
692 if (len == 0)
693 return false; /* no destination? */
695 if (TARGET_Z80_LIKE)
697 while (q>p && *q!=',')
698 q--;
699 if (*q==',')
700 return false; /* conditional jump */
703 if (TARGET_IS_F8 && p[0] == '#')
704 p++;
706 /* now put the destination in %6 */
707 bindVar (6, &p, &vars);
709 return true;
712 /*-----------------------------------------------------------------*/
713 /* okToRemoveSLOC - Check if label %1 is a SLOC and not other */
714 /* usage of it in the code depends on a value from this section */
715 /*-----------------------------------------------------------------*/
716 FBYNAME (okToRemoveSLOC)
718 const lineNode *pl;
719 const char *sloc, *p;
720 int dummy1, dummy2, dummy3;
722 /* assumes that %1 as the SLOC name */
723 sloc = hTabItemWithKey (vars, 1);
724 if (sloc == NULL) return FALSE;
725 p = strstr(sloc, "sloc");
726 if (p == NULL) return FALSE;
727 p += 4;
728 if (sscanf(p, "%d_%d_%d", &dummy1, &dummy2, &dummy3) != 3) return FALSE;
729 /*TODO: ultra-paranoid: get function name from "head" and check that */
730 /* the sloc name begins with that. Probably not really necessary */
732 /* Look for any occurrence of this SLOC before the peephole match */
733 for (pl = currPl->prev; pl; pl = pl->prev) {
734 if (pl->line && !pl->isDebug && !pl->isComment
735 && *pl->line != ';' && strstr(pl->line, sloc))
736 return FALSE;
738 /* Look for any occurrence of this SLOC after the peephole match */
739 for (pl = endPl->next; pl; pl = pl->next) {
740 if (pl->line && !pl->isDebug && !pl->isComment
741 && *pl->line != ';' && strstr(pl->line, sloc))
742 return FALSE;
744 return TRUE; /* safe for a peephole to remove it :) */
747 /*-----------------------------------------------------------------*/
748 /* deadMove - Check, if a pop/push pair can be removed */
749 /*-----------------------------------------------------------------*/
750 FBYNAME (deadMove)
752 const char *reg = hTabItemWithKey (vars, 1);
754 if (port->peep.deadMove)
755 return port->peep.deadMove (reg, currPl, head);
757 fprintf (stderr, "Function deadMove not initialized in port structure\n");
758 return FALSE;
761 /*-----------------------------------------------------------------*/
762 /* labelHashEntry- searches for a label in the list labelHash */
763 /* Builds labelHash, if it does not yet exist. */
764 /* Returns the labelHashEntry or NULL */
765 /*-----------------------------------------------------------------*/
766 labelHashEntry *
767 getLabelRef (const char *label, lineNode *head)
769 labelHashEntry *entry;
771 /* If we don't have the label hash table yet, build it. */
772 if (!labelHash)
774 buildLabelRefCountHash (head);
777 entry = hTabFirstItemWK (labelHash, hashSymbolName (label));
779 while (entry)
781 if (!strcmp (label, entry->name))
783 break;
785 entry = hTabNextItemWK (labelHash);
787 return entry;
790 /* labelRefCount:
792 * takes two parameters: a variable (bound to a label name)
793 * and an expected reference count.
795 * Returns TRUE if that label is defined and referenced exactly
796 * the given number of times.
798 FBYNAME (labelRefCount)
800 int varNumber, expectedRefCount;
801 bool rc = FALSE;
803 if (sscanf (cmdLine, "%*[ \t%]%d %d", &varNumber, &expectedRefCount) == 2)
805 char *label = hTabItemWithKey (vars, varNumber);
807 if (label)
809 labelHashEntry *entry = getLabelRef (label, head);
811 if (entry)
813 #if 0
814 /* debug spew. */
815 fprintf (stderr, "labelRefCount: %s has refCount %d, want %d\n",
816 label, entry->refCount, expectedRefCount);
817 #endif
819 rc = (expectedRefCount == entry->refCount);
821 else
823 // Not a local label. We do not know how often it might be referenced.
824 rc = FALSE;
827 else
829 fprintf (stderr, "*** internal error: var %d not bound"
830 " in peephole labelRefCount rule.\n",
831 varNumber);
834 else
836 fprintf (stderr,
837 "*** internal error: labelRefCount peephole restriction"
838 " malformed: %s\n", cmdLine);
840 return rc;
843 /* labelRefCountChange:
844 * takes two parameters: a variable (bound to a label name)
845 * and a signed int for changing the reference count.
847 * Please note, this function is not a conditional. It unconditionally
848 * changes the label. It should be passed as the 'last' function
849 * so it only is applied if all other conditions have been met.
851 * should always return TRUE
853 FBYNAME (labelRefCountChange)
855 int varNumber, RefCountDelta;
856 bool rc = FALSE;
858 /* If we don't have the label hash table yet, build it. */
859 if (!labelHash)
861 buildLabelRefCountHash (head);
864 if (sscanf (cmdLine, "%*[ \t%]%d %i", &varNumber, &RefCountDelta) == 2)
866 char *label = hTabItemWithKey (vars, varNumber);
868 if (label)
870 labelHashEntry *entry;
872 entry = hTabFirstItemWK (labelHash, hashSymbolName (label));
874 while (entry)
876 if (!strcmp (label, entry->name))
878 break;
880 entry = hTabNextItemWK (labelHash);
882 if (entry)
884 if (0 <= entry->refCount + RefCountDelta)
886 entry->refCount += RefCountDelta;
887 rc = TRUE;
889 else
891 fprintf (stderr, "*** internal error: label %s may not get"
892 " negative refCount in %s peephole.\n",
893 label, __func__);
896 else
898 // Not a local label. We do not know how often it might be referenced.
899 return TRUE;
902 else
904 fprintf (stderr, "*** internal error: var %d not bound"
905 " in peephole %s rule.\n",
906 varNumber, __func__);
909 else
911 fprintf (stderr,
912 "*** internal error: labelRefCountChange peephole restriction"
913 " malformed: %s\n", cmdLine);
915 return rc;
918 /* newLabel creates new dollar-label and returns it in the specified container.
919 * Optional second operand may specify initial reference count, by default 1.
920 * return TRUE if no errors detected
922 FBYNAME (newLabel)
924 int varNumber;
925 unsigned refCount;
926 switch (sscanf (cmdLine, " %%%d %u", &varNumber, &refCount))
928 case 1:
929 refCount = 1;
930 break;
931 case 2:
932 break;
933 default:
934 fprintf (stderr,
935 "*** internal error: newLabel peephole restriction"
936 " malformed: %s\n", cmdLine);
937 return FALSE;
940 if (varNumber <= 0)
942 fprintf (stderr, "*** internal error: invalid container %%%d"
943 " in peephole %s rule.\n",
944 varNumber, __func__);
945 return FALSE;
948 if (labelHash == NULL)
949 buildLabelRefCountHash (head);
951 labelHashEntry *entry;
952 int key;
953 unsigned maxLabel = 100; // do not use labels below than 00100$
954 for (entry = hTabFirstItem (labelHash, &key); entry;
955 entry = hTabNextItem (labelHash, &key))
957 const char *name = entry->name;
958 wassert (name);
959 if (!ISCHARDIGIT (name[0]))
960 continue;
961 if (name[strlen (name)-1] != '$')
962 continue;
963 unsigned n;
964 if (sscanf (name, "%u$", &n) != 1)
965 continue;
966 if (maxLabel < n)
967 maxLabel = n;
969 ++maxLabel;
970 entry = traceAlloc (&_G.labels, Safe_alloc (sizeof (*entry)));
971 int len = snprintf (entry->name, SDCC_NAME_MAX, "%05u$", maxLabel);
972 entry->name[len] = 0;
973 entry->refCount = refCount;
974 hTabAddItem (&labelHash, hashSymbolName (entry->name), entry);
976 char *value = traceAlloc (&_G.values, Safe_strdup(entry->name));
977 hTabAddItem (&vars, varNumber, value);
979 return TRUE;
982 /* Within the context of the lines currPl through endPl, determine
983 ** if the variable var contains a symbol that is volatile. Returns
984 ** TRUE only if it is certain that this was not volatile (the symbol
985 ** was found and not volatile, or var was a constant or CPU register).
986 ** Returns FALSE if the symbol was found and volatile, the symbol was
987 ** not found, or var was a indirect/pointer addressing mode.
989 static bool
990 notVolatileVariable(const char *var, lineNode *currPl, lineNode *endPl)
992 char symname[SDCC_NAME_MAX + 1];
993 char *p = symname;
994 const char *vp = var;
995 lineNode *cl;
996 operand *op;
997 iCode *last_ic;
999 const bool global_not_volatile = currFunc ? !currFunc->funcUsesVolatile : false;
1001 /* Can't tell if indirect accesses are volatile or not, so
1002 ** assume they are (if there is a volatile access in the function at all), just to be safe.
1004 if (TARGET_IS_MCS51 || TARGET_IS_DS390 || TARGET_IS_DS400)
1006 if (*var=='@')
1007 return global_not_volatile;
1009 if (TARGET_Z80_LIKE)
1011 if (var[0] == '#')
1012 return true;
1013 if (var[0] == '(')
1014 return global_not_volatile;
1015 if (strstr (var, "(bc)"))
1016 return global_not_volatile;
1017 if (strstr (var, "(de)"))
1018 return global_not_volatile;
1019 if (strstr (var, "(hl"))
1020 return global_not_volatile;
1021 if (strstr (var, "(ix"))
1022 return global_not_volatile;
1023 if (strstr (var, "(iy"))
1024 return global_not_volatile;
1025 // sm83-specific ldh can be volatile
1026 // but HRAM doesn't have to be volatile
1027 if (TARGET_ID_SM83 && strstr (var, "(c)"))
1028 return global_not_volatile;
1031 if (TARGET_IS_STM8)
1033 if (var[0] == '#')
1034 return true;
1035 if (var[0] == '(')
1036 return global_not_volatile;
1037 if (strstr (var, "(x)"))
1038 return global_not_volatile;
1039 if (strstr (var, "(y)"))
1040 return global_not_volatile;
1041 if (strstr (var, ", x)"))
1042 return global_not_volatile;
1043 if (strstr (var, ", y)"))
1044 return global_not_volatile;
1045 if (strstr (var, ", sp)"))
1046 return global_not_volatile;
1047 if (strchr (var, '[') && strchr (var, ']'))
1048 return global_not_volatile;
1049 if (strstr(var, "0x") || strstr(var, "0X") || isdigit(var[0]))
1050 return global_not_volatile;
1053 if (TARGET_PDK_LIKE)
1055 if (var[0] == '#')
1056 return true;
1057 if (!strcmp (var, "a") || !strcmp (var, "p"))
1058 return true;
1061 if (TARGET_HC08_LIKE || TARGET_IS_MOS6502)
1063 if (var[0] == '#')
1064 return true;
1065 if (strstr(var, "0x") || strstr(var, "0X") || isdigit(var[0]))
1066 return global_not_volatile;
1069 /* Extract a symbol name from the variable */
1070 while (*vp && (*vp!='_'))
1071 vp++;
1072 while (*vp && (ISCHARALNUM(*vp) || *vp=='_'))
1073 *p++ = *vp++;
1074 *p='\0';
1076 if (!symname[0])
1078 /* Nothing resembling a symbol name was found, so it can't
1079 be volatile
1081 return true;
1084 last_ic = NULL;
1085 for (cl = currPl; cl!=endPl->next; cl = cl->next)
1087 if (cl->ic && (cl->ic!=last_ic))
1089 last_ic = cl->ic;
1090 switch (cl->ic->op)
1092 case IFX:
1093 op = IC_COND (cl->ic);
1094 if (IS_SYMOP (op) &&
1095 ( !strcmp(OP_SYMBOL (op)->rname, symname) ||
1096 (OP_SYMBOL (op)->isspilt &&
1097 SPIL_LOC (op) &&
1098 !strcmp(SPIL_LOC (op)->rname, symname)) ))
1100 return !op->isvolatile;
1102 case JUMPTABLE:
1103 op = IC_JTCOND (cl->ic);
1104 if (IS_SYMOP (op) &&
1105 ( !strcmp(OP_SYMBOL (op)->rname, symname) ||
1106 (OP_SYMBOL (op)->isspilt &&
1107 SPIL_LOC (op) &&
1108 !strcmp(SPIL_LOC (op)->rname, symname)) ))
1110 return !op->isvolatile;
1112 default:
1113 op = IC_LEFT (cl->ic);
1114 if (IS_SYMOP (op) &&
1115 ( !strcmp(OP_SYMBOL (op)->rname, symname) ||
1116 (OP_SYMBOL (op)->isspilt &&
1117 SPIL_LOC (op) &&
1118 !strcmp(SPIL_LOC (op)->rname, symname)) ))
1120 return !op->isvolatile;
1122 op = IC_RIGHT (cl->ic);
1123 if (IS_SYMOP (op) &&
1124 ( !strcmp(OP_SYMBOL (op)->rname, symname) ||
1125 (OP_SYMBOL (op)->isspilt &&
1126 SPIL_LOC (op) &&
1127 !strcmp(SPIL_LOC (op)->rname, symname)) ))
1129 return !op->isvolatile;
1131 op = IC_RESULT (cl->ic);
1132 if (IS_SYMOP (op) &&
1133 ( !strcmp(OP_SYMBOL (op)->rname, symname) ||
1134 (OP_SYMBOL (op)->isspilt &&
1135 SPIL_LOC (op) &&
1136 !strcmp(SPIL_LOC (op)->rname, symname)) ))
1138 return !op->isvolatile;
1144 /* Couldn't find the symbol for some reason. Assume volatile if the current function touches anything volatile. */
1145 return global_not_volatile;
1148 /* notVolatile:
1150 * This rule restriction has two different behaviours depending on
1151 * the number of parameters given.
1153 * if notVolatile (no parameters given)
1154 * The rule is applied only if none of the iCodes originating
1155 * the matched pattern reference a volatile operand.
1157 * if notVolatile %1 ... (one or more parameters given)
1158 * The rule is applied if the parameters are not expressions
1159 * containing volatile symbols and are not pointer accesses.
1162 FBYNAME (notVolatile)
1164 int varNumber;
1165 char *var;
1166 char *digitend;
1167 lineNode *cl;
1168 operand *op;
1170 if (!cmdLine)
1172 /* If no parameters given, just scan the iCodes for volatile operands */
1173 for (cl = currPl; cl!=endPl->next; cl = cl->next)
1175 if (cl->ic)
1177 switch (cl->ic->op)
1179 case IFX:
1180 op = IC_COND (cl->ic);
1181 if (IS_SYMOP (op) && op->isvolatile)
1182 return FALSE;
1183 case JUMPTABLE:
1184 op = IC_JTCOND (cl->ic);
1185 if (IS_SYMOP (op) && op->isvolatile)
1186 return FALSE;
1187 default:
1188 op = IC_LEFT (cl->ic);
1189 if (IS_SYMOP (op) && op->isvolatile)
1190 return FALSE;
1191 op = IC_RIGHT (cl->ic);
1192 if (IS_SYMOP (op) && op->isvolatile)
1193 return FALSE;
1194 op = IC_RESULT (cl->ic);
1195 if (IS_SYMOP (op) && op->isvolatile)
1196 return FALSE;
1200 return TRUE;
1203 /* There were parameters; check the volatility of each */
1204 while (*cmdLine && ISCHARSPACE(*cmdLine))
1205 cmdLine++;
1206 while (*cmdLine)
1208 if (*cmdLine!='%')
1209 goto error;
1210 cmdLine++;
1211 if (!ISCHARDIGIT(*cmdLine))
1212 goto error;
1213 varNumber = strtol(cmdLine, &digitend, 10);
1214 cmdLine = digitend;
1215 while (*cmdLine && ISCHARSPACE(*cmdLine))
1216 cmdLine++;
1218 var = hTabItemWithKey (vars, varNumber);
1220 if (var)
1222 if (!notVolatileVariable (var, currPl, endPl))
1223 return false;
1225 else
1227 fprintf (stderr, "*** internal error: var %d not bound"
1228 " in peephole notVolatile rule.\n",
1229 varNumber);
1230 return FALSE;
1234 return TRUE;
1236 error:
1237 fprintf (stderr,
1238 "*** internal error: notVolatile peephole restriction"
1239 " malformed: %s\n", cmdLine);
1240 return FALSE;
1243 /*------------------------------------------------------------------*/
1244 /* setFromConditionArgs - parse a peephole condition's arguments */
1245 /* to produce a set of strings, one per argument. Variables %x will */
1246 /* be replaced with their values. String literals (in single quotes)*/
1247 /* are accepted and return in unquoted form. */
1248 /*------------------------------------------------------------------*/
1249 static set *
1250 setFromConditionArgs (char *cmdLine, hTab * vars)
1252 int varNumber;
1253 char *var;
1254 char *digitend;
1255 set *operands = NULL;
1257 if (!cmdLine)
1258 return NULL;
1260 while (*cmdLine && ISCHARSPACE(*cmdLine))
1261 cmdLine++;
1263 while (*cmdLine)
1265 if (*cmdLine == '%')
1267 cmdLine++;
1268 if (!ISCHARDIGIT(*cmdLine))
1269 goto error;
1270 varNumber = strtol(cmdLine, &digitend, 10);
1271 cmdLine = digitend;
1273 var = hTabItemWithKey (vars, varNumber);
1275 if (var)
1277 addSetHead (&operands, var);
1279 else
1280 goto error;
1282 else if (*cmdLine == '\'' )
1284 char quote = *cmdLine;
1286 var = ++cmdLine;
1287 while (*cmdLine && *cmdLine != quote)
1288 cmdLine++;
1289 if (*cmdLine == quote)
1290 *cmdLine++ = '\0';
1291 else
1292 goto error;
1293 addSetHead (&operands, var);
1295 else
1296 goto error;
1298 while (*cmdLine && ISCHARSPACE(*cmdLine))
1299 cmdLine++;
1302 return operands;
1304 error:
1305 deleteSet (&operands);
1306 return NULL;
1309 static const char *
1310 operandBaseName (const char *op)
1312 if (TARGET_IS_MCS51 || TARGET_IS_DS390 || TARGET_IS_DS400)
1314 if (!strncmp (op, "ar", 2) && ISCHARDIGIT(*(op+2)) && !*(op+3))
1315 return op+1;
1316 if (!strcmp (op, "ab"))
1317 return "ab";
1318 if (!strcmp (op, "acc") || !strncmp (op, "acc.", 4) || *op == 'a')
1319 return "a";
1320 // bug 1739475, temp fix
1321 if (op[0] == '@')
1322 return operandBaseName(op+1);
1324 if (TARGET_Z80_LIKE)
1326 if (!strcmp (op, "d") || !strcmp (op, "e") || !strcmp (op, "(de)"))
1327 return "de";
1328 if (!strcmp (op, "b") || !strcmp (op, "c") || !strcmp (op, "(bc)"))
1329 return "bc";
1330 if (!strcmp (op, "h") || !strcmp (op, "l") || !strcmp (op, "(hl)") || !strcmp (op, "(hl+)") || !strcmp (op, "(hl-)"))
1331 return "hl";
1332 if (!strcmp (op, "iyh") || !strcmp (op, "iyl") || strstr (op, "iy"))
1333 return "iy";
1334 if (!strcmp (op, "ixh") || !strcmp (op, "ixl") || strstr (op, "ix"))
1335 return "ix";
1336 if (!strcmp (op, "a"))
1337 return "af";
1340 return op;
1343 /*------------------------------------------------------------------*/
1344 /* optimizeFor - check optimization conditions */
1345 /* valid parameters: */
1346 /* code-size -> checks for optimize.codeSize > 0 */
1347 /* code-speed -> checks for optimize.codeSpeed > 0 */
1348 /* add the ! symbol before each parameter to negate the condition */
1349 /* combinations with multiple parameters */
1350 /* '!code-speed' '!code-size': apply for balanced opt. */
1351 /* '!code-size': apply for balanced and when optimizing for speed */
1352 /* '!code-speed': apply unless optimizing for code speed */
1353 /*------------------------------------------------------------------*/
1354 FBYNAME (optimizeFor)
1356 const char *cond;
1357 int speed = 0, size = 0; // 0: nothing requested, >0 optimization requested, <0 negated optimization requested
1359 bool ret = false, error = false;
1361 set *operands = setFromConditionArgs (cmdLine, vars);
1363 if (!operands)
1365 fprintf (stderr,
1366 "*** internal error: optimizeFor peephole restriction"
1367 " requires operand(s): %s\n", cmdLine);
1368 return false;
1371 // Loop through all conditions to check requested optimizations
1372 for (cond = setFirstItem (operands); !error && cond != NULL; cond = setNextItem (operands))
1374 const char *condTextSpeed = strstr (cond, "code-speed");
1375 const char *condTextSize = strstr (cond, "code-size");
1376 const char *condNegated = strstr (cond, "!");
1377 const char *condText = condTextSpeed ? condTextSpeed : condTextSize;
1379 // Check for invalid conditions or invalid combinations in the same string
1380 if (!condText || condTextSpeed && condTextSize || condNegated && (condNegated + 1 != condText))
1382 error = true;
1383 break;
1385 if (condTextSize)
1387 if (size == 0)
1388 size = condNegated ? -1 : 1;
1389 else
1390 error = true;
1392 else
1394 if (speed == 0)
1395 speed = condNegated ? -1 : 1;
1396 else
1397 error = true;
1400 // check error, invalid combination of both speed and size or nothing
1401 if (error || (speed != -1) && (speed == size) )
1403 fprintf (stderr,
1404 "*** internal error: optimizeFor peephole restriction"
1405 " malformed: %s\n", cmdLine);
1406 error = true;
1408 else
1409 { // Check conditions and generate return value
1410 ret = true;
1411 if (speed != 0)
1412 ret &= (speed < 0) ^ (optimize.codeSpeed > 0);
1414 if (size != 0)
1415 ret &= (size < 0) ^ (optimize.codeSize > 0);
1418 deleteSet(&operands);
1419 return (ret);
1422 /*-----------------------------------------------------------------*/
1423 /* notUsed - Check, if values in all registers are not read again */
1424 /*-----------------------------------------------------------------*/
1425 FBYNAME (notUsed)
1427 const char *what;
1428 bool ret;
1430 if (!port->peep.notUsed)
1432 fprintf (stderr, "Function notUsed not initialized in port structure\n");
1433 return FALSE;
1436 set *operands = setFromConditionArgs (cmdLine, vars);
1438 if (!operands)
1440 fprintf (stderr,
1441 "*** internal error: notUsed peephole restriction"
1442 " requires operand(s): %s\n", cmdLine);
1443 return FALSE;
1446 what = setFirstItem (operands);
1447 for (ret = TRUE; ret && what != NULL; what = setNextItem (operands))
1448 ret = port->peep.notUsed (what, endPl, head);
1450 deleteSet(&operands);
1452 return (ret);
1455 /*-----------------------------------------------------------------*/
1456 /* notUsedFrom - Check, if values in registers are not read again */
1457 /* starting from label */
1458 /* Registers are checked from left to right */
1459 /*-----------------------------------------------------------------*/
1460 FBYNAME (notUsedFrom)
1462 const char *what, *label;
1463 bool ret;
1465 if (!port->peep.notUsedFrom)
1467 fprintf (stderr, "Function notUsedFrom not initialized in port structure\n");
1468 return false;
1471 set *operands = setFromConditionArgs (cmdLine, vars);
1473 if (!operands)
1475 fprintf (stderr,
1476 "*** internal error: notUsedFrom peephole restriction"
1477 " requires operand(s): %s\n", cmdLine);
1478 return false;
1480 if (elementsInSet(operands) < 2)
1482 fprintf (stderr,
1483 "*** internal error: notUsedFrom peephole restriction"
1484 " malformed: %s\n", cmdLine);
1485 deleteSet(&operands);
1486 return false;
1489 operands = reverseSet(operands);
1491 label = setFirstItem (operands);
1492 what = setNextItem (operands);
1494 for (ret = true; ret && what; what = setNextItem (operands))
1495 ret = port->peep.notUsedFrom (what, label, head);
1497 deleteSet(&operands);
1499 return (ret);
1502 /*-----------------------------------------------------------------*/
1503 /* unusedReg - find first unused register from specified list and */
1504 /* assign to container specified as first argument. Fails if all */
1505 /* of specified registers are accessed for reading. */
1506 /*-----------------------------------------------------------------*/
1507 FBYNAME (unusedReg)
1509 int dst;
1510 int n;
1511 if (sscanf (cmdLine, " %%%d%n", &dst, &n) != 1 || dst <= 0)
1513 fprintf (stderr,
1514 "*** internal error: unusedReg peephole restriction"
1515 " malformed: %s\n", cmdLine);
1516 return FALSE;
1519 set *operands = setFromConditionArgs (&cmdLine[n], vars);
1520 if (!operands || elementsInSet (operands) < 2 || elementsInSet (operands) > 3)
1522 fprintf (stderr,
1523 "*** internal error: unusedReg peephole restriction"
1524 " malformed: %s\n", cmdLine);
1525 return FALSE;
1528 char *what = setFirstItem (operands);
1529 for (; what != NULL; what = setNextItem (operands))
1530 if (port->peep.notUsed (what, endPl, head))
1531 break;
1533 bool ret = (what != NULL);
1534 if (ret)
1536 char *s[] = {what, NULL};
1537 bindVar (dst, s, &vars);
1540 deleteSet (&operands);
1542 return ret;
1545 /*-----------------------------------------------------------------*/
1546 /* canAssign - Check, if we can do ld dst, src. */
1547 /*-----------------------------------------------------------------*/
1548 FBYNAME (canAssign)
1550 set *operands;
1551 const char *dst, *src, *exotic;
1553 operands = setFromConditionArgs (cmdLine, vars);
1555 if (!operands || elementsInSet(operands) < 2 || elementsInSet(operands) > 3)
1557 fprintf (stderr,
1558 "*** internal error: canAssign peephole restriction"
1559 " malformed: %s\n", cmdLine);
1560 return FALSE;
1563 if(elementsInSet(operands) == 3)
1565 exotic = setFirstItem (operands);
1566 src = setNextItem (operands);
1567 dst = setNextItem (operands);
1569 else
1571 exotic = 0;
1572 src = setFirstItem (operands);
1573 dst = setNextItem (operands);
1576 if (port->peep.canAssign)
1578 bool ret = port->peep.canAssign (dst, src, exotic);
1579 deleteSet (&operands);
1580 return (ret);
1583 deleteSet (&operands);
1585 fprintf (stderr, "Function canAssign not initialized in port structure\n");
1586 return FALSE;
1589 /*-----------------------------------------------------------------*/
1590 /* canJoinRegs - joins set of registers to combined one, returns */
1591 /* true, if result register is valid. First operand can be */
1592 /* 'unordered' if order of registers is not sufficient. Last */
1593 /* operand should be wildcard. If result is not required, then */
1594 /* wildcard should be %0. If some of source registers is not */
1595 /* sufficient then empty string can be passed. */
1596 /*-----------------------------------------------------------------*/
1597 FBYNAME (canJoinRegs)
1599 // Must be specified at least 3 parameters: reg_hi reg_lo and dst
1600 // If destination is not required, then %0 should be specified
1601 if (!port->peep.canJoinRegs)
1603 fprintf (stderr, "Function canJoinRegs not supported by the port\n");
1604 return FALSE;
1607 int dstKey;
1608 int i;
1609 for (i = strlen (cmdLine)-1; i >= 0 && ISCHARSPACE (cmdLine[i]); --i)
1611 for (; i >= 0 && !ISCHARSPACE (cmdLine[i]); --i)
1613 if (i < 0 || cmdLine[i+1] != '%' || (cmdLine[i+1] && (sscanf (&cmdLine[i+2], "%d", &dstKey) != 1 || dstKey < 0)))
1615 fprintf (stderr,
1616 "*** internal error: canJoinRegs peephole restriction"
1617 " has bad result container: %s\n", &cmdLine[i+1]);
1618 return FALSE;
1620 //parse cmd line without last operand
1621 cmdLine[i] = '\0';
1622 set *operands = setFromConditionArgs (cmdLine, vars);
1623 cmdLine[i] = ' ';
1625 if (operands == NULL)
1627 fprintf (stderr,
1628 "*** internal error: canJoinRegs peephole restriction"
1629 " malformed: %s\n", cmdLine);
1630 return FALSE;
1633 bool unordered = false;
1634 const char *first = setFirstItem (operands);
1635 if (first && !strcmp (first, "unordered"))
1637 unordered = true;
1638 deleteSetItem (&operands, (void*)first);
1641 int size = elementsInSet (operands);
1642 if (size < 2)
1644 fprintf (stderr,
1645 "*** internal error: canJoinRegs peephole restriction"
1646 " requires at least 3 operands: %s\n", cmdLine);
1647 return FALSE;
1650 const char **regs = (const char**) Safe_alloc ( (size + 1) * sizeof (*regs));
1651 i = size;
1652 regs[size] = NULL; /* end of registers */
1653 //fill regs reversing order (operands have reversed order)
1654 for (set *it = operands; it; it = it->next)
1655 regs[--i] = (const char*)it->item;
1657 //if unordered specified, then sort elements by ascending order
1658 if (unordered)
1659 qsort (regs, size, sizeof (*regs), (int (*)(const void*,const void*))&strcmp);
1661 char dst[20];
1662 bool result;
1663 for (;;)
1665 result = port->peep.canJoinRegs (regs, dst);
1666 if (result || !unordered)
1667 break;
1669 //do next registers permutation
1670 int i;
1671 //find last regs[i] < regs[i+1]
1672 for (i = size-2; i >= 0; --i)
1673 if (strcmp (regs[i+1], regs[i]) > 0)
1674 break;
1675 if (i < 0)
1676 break; /* was last permutation */
1678 int j;
1679 //find last regs[j] > regs[i], where j > i
1680 for (j = size-1; j > i; --j)
1681 if (strcmp (regs[j], regs[i]) > 0)
1682 break;
1684 //swap regs[j] and regs[i]
1685 const char *t = regs[i];
1686 regs[i] = regs[j];
1687 regs[j] = t;
1688 //reverse order from j+1 to end
1689 for (j = j+1, i = size - 1; j < i; ++j, --i)
1691 t = regs[j];
1692 regs[j] = regs[i];
1693 regs[i] = t;
1697 Safe_free (regs);
1699 if (result && dstKey > 0)
1701 char *s[] = { dst, NULL };
1702 bindVar (dstKey, s, &vars);
1705 deleteSet (&operands);
1706 return result;
1709 /*-----------------------------------------------------------------*/
1710 /* canSplitReg - returns true, if register can be splitted. First */
1711 /* operand contains complex register name and is required. Other */
1712 /* operands should be wildcards. If result is not sufficient then */
1713 /* they can be omited. */
1714 /*-----------------------------------------------------------------*/
1715 FBYNAME (canSplitReg)
1717 if (!port->peep.canSplitReg)
1719 fprintf (stderr, "Function canSplitReg not supported by the port\n");
1720 return FALSE;
1723 int i;
1724 //find start of first operand
1725 for (i = 0; cmdLine[i] && ISCHARSPACE (cmdLine[i]); ++i)
1727 if (cmdLine[i] == '\0')
1729 fprintf (stderr,
1730 "*** internal error: canSplitReg peephole restriction"
1731 " malformed: %s\n", cmdLine);
1732 return FALSE;
1735 //find end of first operand
1736 for (; cmdLine[i] && !ISCHARSPACE (cmdLine[i]); ++i)
1739 //parse first operand
1740 char t = cmdLine[i];
1741 cmdLine[i] = '\0';
1742 set *operands = setFromConditionArgs (cmdLine, vars);
1743 cmdLine[i] = t;
1744 if (cmdLine[i] == '\0')
1746 fprintf (stderr,
1747 "*** internal error: canSplitReg peephole restriction"
1748 " malformed: %s\n", cmdLine);
1749 return FALSE;
1752 //scan remaining operands
1753 int size = 2;
1754 int *varIds = (int*)Safe_alloc (size * sizeof(*varIds));
1755 const char *cl = &cmdLine[i+1];
1756 for (i = 0;; ++i)
1758 if (i >= size)
1760 size *= 2;
1761 varIds = (int*)Safe_realloc (varIds, size * sizeof(*varIds));
1763 int len;
1764 if (sscanf (cl, " %%%d%n", &varIds[i], &len) != 1)
1765 break;
1766 if (varIds[i] < 0)
1768 fprintf (stderr,
1769 "*** internal error: canSplitReg peephole restriction"
1770 " has invalid destination container: %s\n", cmdLine);
1771 return FALSE;
1773 cl += len;
1775 size = i;
1776 char (*dst)[16];
1777 dst = Safe_alloc (size * sizeof (*dst));
1778 bool ret = port->peep.canSplitReg ((char*)setFirstItem (operands), dst, size);
1779 for (i = 0; ret && i < size; ++i)
1781 if (varIds[i] <= 0)
1782 continue;
1783 char *s[] = { dst[i], NULL };
1784 bindVar (varIds[i], s, &vars);
1786 Safe_free (dst);
1787 Safe_free (varIds);
1788 deleteSet (&operands);
1789 return ret;
1792 /*-----------------------------------------------------------------*/
1793 /* operandsNotRelated - returns true if the condition's operands */
1794 /* are not related (taking into account register name aliases). */
1795 /* N-way comparison performed between all operands. */
1796 /*-----------------------------------------------------------------*/
1797 FBYNAME (operandsNotRelated)
1799 set *operands;
1800 const char *op1, *op2;
1802 operands = setFromConditionArgs (cmdLine, vars);
1804 if (!operands)
1806 fprintf (stderr,
1807 "*** internal error: operandsNotRelated peephole restriction"
1808 " malformed: %s\n", cmdLine);
1809 return FALSE;
1812 bool ret = true;
1813 while ((op1 = setFirstItem (operands)))
1815 deleteSetItem (&operands, (void*)op1);
1816 op1 = operandBaseName (op1);
1818 for (op2 = setFirstItem (operands); op2; op2 = setNextItem (operands))
1820 if ((strchr(op1, '(') || strchr(op1, '[')) && (strchr(op2, '(') || strchr(op2, '['))) // Might be the same or overlapping memory locations; err on the safe side.
1822 ret = false;
1823 goto done;
1826 op2 = operandBaseName (op2);
1827 if (strcmp (op1, op2) == 0)
1829 ret = false;
1830 goto done;
1833 if (TARGET_IS_MCS51 || TARGET_IS_DS390 || TARGET_IS_DS400)
1835 /* handle overlapping 'dptr' vs. { 'dpl', 'dph' } */
1836 if (!strcmp (op1, "dptr") && (!strcmp (op2, "dpl") || !strcmp (op2, "dph")) ||
1837 !strcmp (op2, "dptr") && (!strcmp (op1, "dpl") || !strcmp (op1, "dph")) ||
1838 !strcmp (op1, "ab") && (!strcmp (op2, "a") || !strcmp (op2, "b")) ||
1839 !strcmp (op2, "ab") && (!strcmp (op1, "a") || !strcmp (op1, "b")))
1841 ret = false;
1842 goto done;
1848 done:
1849 deleteSet (&operands);
1850 return ret;
1853 /*-----------------------------------------------------------------*/
1854 /* notSimilar - Check, if one is another's substring */
1855 /*-----------------------------------------------------------------*/
1856 FBYNAME (notSimilar)
1858 set *operands;
1859 const char *op1, *op2;
1861 operands = setFromConditionArgs (cmdLine, vars);
1863 if (!operands)
1865 fprintf (stderr,
1866 "*** internal error: notSimilar peephole restriction"
1867 " malformed: %s\n", cmdLine);
1868 return FALSE;
1871 while ((op1 = setFirstItem (operands)))
1873 deleteSetItem (&operands, (void*)op1);
1875 for (op2 = setFirstItem (operands); op2; op2 = setNextItem (operands))
1877 if (strstr (op1, op2) || strstr (op2, op1))
1879 deleteSet (&operands);
1880 return FALSE;
1885 deleteSet (&operands);
1886 return TRUE;
1889 /*-----------------------------------------------------------------*/
1890 /* symmParmStack - Caller readjusts stack by the number of bytes
1891 that were pushed in all calls to this function */
1892 /*-----------------------------------------------------------------*/
1893 FBYNAME (symmParmStack)
1895 set *operands = setFromConditionArgs (cmdLine, vars);
1897 if (!operands)
1899 fprintf (stderr,
1900 "*** internal error: symmParmStack peephole restriction"
1901 " requires operand: %s\n", cmdLine);
1902 return FALSE;
1905 const char *name = setFirstItem (operands);
1907 bool ret = false;
1909 if (port->peep.symmParmStack)
1910 return port->peep.symmParmStack (name);
1911 deleteSet(&operands);
1913 return ret;
1916 /*-----------------------------------------------------------------*/
1917 /* notSame - Check, that arguments are pairwise not the same */
1918 /*-----------------------------------------------------------------*/
1919 FBYNAME (notSame)
1921 set *operands;
1922 const char *op1, *op2;
1924 operands = setFromConditionArgs (cmdLine, vars);
1926 if (!operands)
1928 fprintf (stderr,
1929 "*** internal error: notSame peephole restriction"
1930 " malformed: %s\n", cmdLine);
1931 return FALSE;
1934 while ((op1 = setFirstItem (operands)))
1936 deleteSetItem (&operands, (void*)op1);
1938 for (op2 = setFirstItem (operands); op2; op2 = setNextItem (operands))
1940 if (strcmp (op1, op2) == 0)
1942 deleteSet (&operands);
1943 return FALSE;
1948 deleteSet (&operands);
1949 return TRUE;
1952 /*-----------------------------------------------------------------*/
1953 /* same - Check if first operand matches any of the remaining */
1954 /*-----------------------------------------------------------------*/
1955 FBYNAME (same)
1957 set *operands;
1958 const char *match, *op;
1960 operands = setFromConditionArgs(cmdLine, vars);
1962 if (!operands)
1964 fprintf(stderr,
1965 "*** internal error: same peephole restriction"
1966 " malformed: %s\n", cmdLine);
1967 return FALSE;
1970 operands = reverseSet(operands);
1972 match = setFirstItem(operands);
1973 for (op = setNextItem(operands); op; op = setNextItem(operands))
1975 if (strcmp(match, op) == 0)
1977 deleteSet(&operands);
1978 return TRUE;
1982 deleteSet(&operands);
1983 return FALSE;
1986 /*-----------------------------------------------------------------*/
1987 /* strIsSymbol - returns true if the parameter is a symbol */
1988 /* That is: an underscore followed by one or more chars */
1989 /*-----------------------------------------------------------------*/
1990 static bool
1991 strIsSymbol(const char *str)
1993 return *str == '_' && str[1] != '\0';
1996 /*-----------------------------------------------------------------*/
1997 /* strIsLiteral - returns true if the parameter is a literal */
1998 /* Checks these formats: binary, octal, decimal, hexadecimal */
1999 /* Skips preceding signs. */
2000 /*-----------------------------------------------------------------*/
2001 static bool
2002 strIsLiteral(const char *str)
2004 const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
2005 unsigned char base = 10;
2006 unsigned char validDigits = 0;
2008 // has to start with a number or a sign
2009 if(!isdigit( (unsigned char)(*str) ) && (*str) != '-' && (*str) != '+')
2010 return false;
2011 // skip sign
2012 if ((*str) == '-' || (*str) == '+')
2013 str++;
2015 // handle 0b 0o 0d 0x
2016 if((*str) == '0')
2018 const char nextChar = tolower((unsigned char)(str[1]));
2019 validDigits = 0;
2020 if (nextChar == 'b')
2022 base = 2;
2023 ++str;
2025 else if(nextChar == 'o')
2027 base = 8;
2028 ++str;
2030 else if(nextChar == 'd')
2032 base = 10;
2033 ++str;
2035 else if(nextChar == 'x')
2037 base = 16;
2038 ++str;
2040 else
2041 validDigits = 1; // the first '0' is a valid digit
2043 ++str;
2046 while((unsigned char)(*str) != '\0'){
2047 unsigned char i;
2048 for(i = 0; i < base; ++i){
2049 if(tolower((unsigned char)(*str)) == digits[i])
2050 break;
2052 // number was too big or not valid
2053 if(i >= base)
2054 return false;
2055 ++validDigits;
2056 ++str;
2058 return validDigits > 0;
2061 /*-----------------------------------------------------------------*/
2062 /* operandsLiteral - returns true if the condition's operands are */
2063 /* literals. */
2064 /*-----------------------------------------------------------------*/
2065 FBYNAME (operandsLiteral)
2067 set *operands = setFromConditionArgs (cmdLine, vars);
2068 const char *op;
2070 if (!operands)
2072 fprintf (stderr,
2073 "*** internal error: operandsLiteral peephole restriction"
2074 " malformed: %s\n", cmdLine);
2075 return FALSE;
2078 for (op = setFirstItem (operands); op; op = setNextItem (operands))
2080 if (!strIsLiteral(op))
2082 deleteSet (&operands);
2083 return false;
2087 deleteSet (&operands);
2088 return true;
2091 /*-----------------------------------------------------------------*/
2092 /* strIsLiteralOrSymbol - returns true if the parameter is a */
2093 /* literal or compiler symbol */
2094 /*-----------------------------------------------------------------*/
2095 static bool
2096 strIsLiteralOrSymbol(const char *str)
2098 return strIsSymbol(str) || strIsLiteral(str);
2101 /*-----------------------------------------------------------------*/
2102 /* operandsLitOrSym - returns true if the condition's operands are */
2103 /* literals or compiler symbols. */
2104 /*-----------------------------------------------------------------*/
2105 FBYNAME (operandsLitOrSym)
2107 set *operands;
2108 const char *op;
2110 operands = setFromConditionArgs (cmdLine, vars);
2112 if (!operands)
2114 fprintf (stderr,
2115 "*** internal error: operandsLitOrSym peephole restriction"
2116 " malformed: %s\n", cmdLine);
2117 return false;
2120 for (op = setFirstItem (operands); op; op = setNextItem (operands))
2122 if (!strIsLiteralOrSymbol(op))
2124 deleteSet (&operands);
2125 return false;
2129 deleteSet (&operands);
2130 return true;
2133 /*-----------------------------------------------------------------*/
2134 /* removeParentheses */
2135 /* First operand: parameter to be parsed */
2136 /* Second operand: result of conversion */
2137 /* The function removes the input parameter parentheses if present,*/
2138 /* else it copies the input directly to the output. */
2139 /* returns true if the input has not parentheses. */
2140 /* returns true if it has parametheses at first and last chars */
2141 /* and there was no other error */
2142 /*-----------------------------------------------------------------*/
2143 FBYNAME (removeParentheses)
2145 int dstKey;
2146 int i;
2148 // Find space previous to last operand
2149 for (i = strlen (cmdLine)-1; i >= 0 && ISCHARSPACE (cmdLine[i]); --i)
2151 for (; i >= 0 && !ISCHARSPACE (cmdLine[i]); --i)
2153 if (i < 0 || cmdLine[i+1] != '%' || (cmdLine[i+1] && (sscanf (&cmdLine[i+2], "%d", &dstKey) != 1 || dstKey < 0)))
2155 fprintf (stderr,
2156 "*** internal error: removeParentheses peephole restriction"
2157 " has bad result container: %s\n", &cmdLine[i+1]);
2158 return false;
2160 //Parse cmd line without last operand
2161 cmdLine[i] = '\0';
2162 set *operands = setFromConditionArgs (cmdLine, vars);
2163 cmdLine[i] = ' '; // Restore space
2165 if (!operands || elementsInSet(operands) > 1)
2167 fprintf (stderr,
2168 "*** internal error: removeParentheses peephole restriction"
2169 " malformed: %s\n", cmdLine);
2170 return false;
2173 // Parse the operand and remove the parenthesis
2174 char r[128];
2175 const char *op = setFirstItem (operands);
2177 if (*op == '(')
2179 if(op[strlen(op)-1] == ')')
2180 op++; // Skip start parenthesis
2181 else
2183 // Abort if no matching closing parenthesis
2184 deleteSet (&operands);
2185 return false;
2188 if (strlen(op) > 127) // Abort if string does not fit in buffer
2190 deleteSet (&operands);
2191 return false;
2194 // Do the copy and skip ending parenthesis
2195 i = 0;
2196 while (*op)
2198 if (*op != ')')
2200 r[i++] = *op++;
2202 else
2204 op++;
2205 break;
2208 r[i] = '\0';
2210 // Abort if remaining chars in source or no chars copied into result string
2211 if ((*op) || (i == 0))
2213 deleteSet (&operands);
2214 return false;
2217 char *p[] = {r, NULL};
2218 bindVar (dstKey, p, &vars);
2220 deleteSet (&operands);
2221 return true;
2224 static long *
2225 immdGet (const char *pc, long *pl)
2227 long s = 1;
2229 if (!pc || !pl)
2230 return NULL;
2232 // omit space
2233 for (; ISCHARSPACE (*pc); pc++);
2234 // parse sign
2235 for (; !ISCHARDIGIT (*pc); pc++)
2236 if (*pc == '-')
2237 s *= -1;
2238 else if (*pc == '+')
2239 s *= +1;
2240 else
2241 return NULL;
2243 if (pc[0] == '0' && (pc[1] == 'x' || pc[1] == 'X'))
2245 if (sscanf (pc + 2, "%lx", pl) != 1)
2246 return NULL;
2248 else
2250 if (sscanf (pc, "%ld", pl) != 1)
2251 return NULL;
2254 *pl *= s;
2255 return pl;
2258 static bool
2259 immdError (const char *info, const char *param, const char *cmd)
2261 fprintf (stderr, "*** internal error: immdInRange gets "
2262 "%s: \"%s\" in \"%s\"\n", info, param, cmd);
2263 return FALSE;
2266 /*-----------------------------------------------------------------*/
2267 /* isPowerOfTwo - true if n is a power of 2 */
2268 /*-----------------------------------------------------------------*/
2269 static bool
2270 isPowerOfTwo(unsigned long n)
2272 return (n != 0) && ((n & (n - 1)) == 0);
2275 /*-----------------------------------------------------------------*/
2276 /* findBitPosition - Returns the bit position set or cleared in n */
2277 /* Parameters: */
2278 /* n: value to be tested. */
2279 /* bits: number of positions to test. */
2280 /* complement: when true, the number must be bitwise complemented */
2281 /* Returns: */
2282 /* -2 if bits is out of valid range (1..32) */
2283 /* -1 if n has more than one bit set or cleared */
2284 /* -1 if n has bits in positions over the bits param */
2285 /* bit position (starting at 0) when only 1 bit set or clear */
2286 /* Examples: */
2287 /* n=0x02, bits=8, complemented=false -> 1 */
2288 /* n=0x7F, bits=8, complemented=true -> 7 */
2289 /*-----------------------------------------------------------------*/
2290 static int
2291 findBitPosition(unsigned long n, unsigned long bits, bool complement)
2293 unsigned long mask;
2294 int bitPos;
2296 if ((bits < 1) || (bits > 32)) //bits out of range?
2297 return -2;
2298 mask = (1ULL << bits) -1;
2299 if (n != (n & mask)) // bits outside mask?
2300 return -1;
2302 if (complement)
2303 n = (~n) & mask;
2304 if (!isPowerOfTwo (n)) // Not valid if more than one bit is set
2305 return -1;
2307 bitPos = -1;
2308 // One by one move the only set bit to right till it reaches end
2309 while (n)
2311 n >>= 1;
2312 bitPos++; //count number of shifts
2315 return bitPos;
2318 /*-----------------------------------------------------------------*/
2319 /* swapOperation - Calculates a swap operation with given params */
2320 /* Parameters: */
2321 /* n: value to be swapped. */
2322 /* bits: length of value to be swapped, in bits. */
2323 /* Returns 0 if no error: */
2324 /* -2 if bits is out of valid range: even numbers (2..32) */
2325 /* -1 if n has bits in positions over the bits param */
2326 /*-----------------------------------------------------------------*/
2327 static int
2328 swapOperation (unsigned long n, unsigned long bits, unsigned long * result)
2330 unsigned long mask = (1ULL << bits) -1;
2331 unsigned int shift = bits / 2;
2333 if ((bits < 1) || (bits > 32) || (bits & 0x01)) // bits out of range or odd
2334 return -2;
2335 if (n != (n & mask)) // bits outside mask?
2336 return -1;
2338 *result = (((n << shift) | (n >> shift)) & mask);
2339 return 0; // no error.
2342 /*-----------------------------------------------------------------*/
2343 /* stringMatchesOperator - returns true if 'str' matches 'op' */
2344 /* 'str' matches 'op' if they contain the same string */
2345 /* 'str' also matches if surrounded by quotes or double quotes */
2346 /*-----------------------------------------------------------------*/
2347 static bool
2348 stringMatchesOperator (const char * str, const char *op)
2350 if (str && op)
2352 if (strcmp(str, op) == 0)
2354 return true;
2356 else
2358 size_t length = strlen(str);
2359 // Check if quotes are present and they are the same at start and end.
2360 if ((length >= 2) && ((str[0] == '\'') || (str[0] == '\"')) && (str[0] == str[length-1]))
2361 return strncmp(&str[1], op, length-2) == 0;
2364 return false;
2366 /*-----------------------------------------------------------------*/
2367 /* immdInRange - returns true if the result of a given operation */
2368 /* of two immediates is in a give range. */
2369 /*-----------------------------------------------------------------*/
2370 FBYNAME (immdInRange)
2372 char r[64], operator[24];
2373 const char *op;
2374 long i, j, k, h, low, high, left_l, right_l, order;
2376 for (i = order = 0; order < 6;)
2378 // pick up one parameter in the temp buffer r[64]
2379 for (; ISCHARSPACE (cmdLine[i]) && cmdLine[i]; i++);
2380 for (j = i; !ISCHARSPACE (cmdLine[j]) && cmdLine[j]; j++);
2381 if (!cmdLine[i]) // unexpected end
2382 return immdError ("no enough input", "", cmdLine);
2383 else if(j >= 64)
2384 return immdError ("buffer overflow", "", cmdLine);
2385 else
2387 for (k = i; k < j; k++)
2388 r[k - i] = cmdLine[k];
2389 r[j - i] = 0;
2391 // parse the string by order
2392 switch (order)
2394 case 0: // lower bound
2395 if (!immdGet (r, &low))
2396 return immdError ("bad lower bound", r, cmdLine);
2397 break;
2398 case 1: // upper bound
2399 if (!immdGet (r, &high))
2400 return immdError ("bad upper bound", r, cmdLine);
2401 break;
2402 case 2: // operator
2403 if (sscanf (r, "%23s", operator) != 1)
2404 return immdError ("bad operator", r, cmdLine);
2405 break;
2406 case 3: // left operand
2407 if (immdGet (r, &left_l)) // the left operand is given directly
2410 else if (r[0] == '%') // the left operand is passed via pattern match
2412 if (!immdGet (r + 1, &k) || !(op = hTabItemWithKey (vars, (int) k)))
2413 return immdError ("bad left operand", r, cmdLine);
2414 else if (!immdGet (op, &left_l))
2415 return FALSE;
2417 else
2418 return immdError ("bad left operand", r, cmdLine);
2419 break;
2420 case 4: // right operand
2421 if (immdGet (r, &right_l)) // the right operand is given directly
2424 else if (r[0] == '%') // the right operand is passed via pattern match
2426 if (!immdGet (r + 1, &k) || !(op = hTabItemWithKey (vars, (int) k)))
2427 return immdError ("bad right operand", r, cmdLine);
2428 else if (!immdGet (op, &right_l))
2429 return immdError ("bad right operand", op, r);
2431 else
2432 return immdError ("bad right operand", r, cmdLine);
2433 break;
2434 case 5: // result
2435 if (r[0] != '%' || !(immdGet (r + 1, &h) || (r[1] == 'x' && immdGet (r + 2, &h))))
2436 return immdError ("bad result container", r, cmdLine);
2437 break;
2438 default: // should not reach
2439 return immdError ("unexpected input", "", cmdLine);
2440 break;
2442 order++;
2443 i = j;
2446 // calculate
2447 if (stringMatchesOperator (operator, "+")) // add
2449 i = left_l + right_l;
2451 else if (stringMatchesOperator (operator, "-")) // sub
2453 i = left_l - right_l;
2455 else if (stringMatchesOperator (operator, "*")) // mul
2457 i = left_l * right_l;
2459 else if (stringMatchesOperator (operator, "/")) // div
2461 if (right_l == 0)
2462 return immdError ("division by zero", "", cmdLine);
2463 i = left_l / right_l;
2465 else if (stringMatchesOperator (operator, "%")) // mod
2467 if (right_l == 0)
2468 return immdError ("division by zero", "", cmdLine);
2469 i = left_l % right_l;
2471 else if (stringMatchesOperator (operator, "&")) // and
2473 i = left_l & right_l;
2475 else if (stringMatchesOperator (operator, "^")) // xor
2477 i = left_l ^ right_l;
2479 else if (stringMatchesOperator (operator, "|")) // or
2481 i = left_l | right_l;
2483 else if (stringMatchesOperator (operator, "singleSetBit") || stringMatchesOperator (operator, "singleResetBit")) // singleSetBit - singleResetBit
2485 i = findBitPosition(left_l, right_l, stringMatchesOperator (operator, "singleResetBit"));
2486 if(i < -1 )
2487 return immdError ("bad right operand", operator, cmdLine);
2488 if(i < 0)
2489 return false;
2491 else if (stringMatchesOperator (operator, "swap")) // swap
2493 if (swapOperation(left_l, right_l, (unsigned long *)&i) != 0)
2494 return immdError ("bad right operand", operator, cmdLine);
2496 else
2497 return immdError ("bad operator", operator, cmdLine);
2499 // bind the result
2500 if ((low <= i && i <= high) || (high <= i && i <= low))
2502 bool hex = false;
2503 if(r[1] == 'x'){
2504 hex = true;
2505 r[1] = '0';
2507 char *p[] = {r, NULL};
2508 if(!hex)
2509 sprintf (r, "%ld", i);
2510 else
2511 sprintf (r, "0x%lx", i);
2512 bindVar ((int) h, p, &vars);
2513 return TRUE;
2515 else
2517 return FALSE;
2521 /*-----------------------------------------------------------------*/
2522 /* inSequence - Check that numerical constants are in sequence */
2523 /*-----------------------------------------------------------------*/
2524 FBYNAME (inSequence)
2526 set *operands;
2527 const char *op;
2528 long seq, val, stride;
2530 if ((operands = setFromConditionArgs(cmdLine, vars)) == NULL)
2532 fprintf (stderr,
2533 "*** internal error: inSequence peephole restriction"
2534 " malformed: %s\n", cmdLine);
2535 return FALSE;
2538 operands = reverseSet(operands);
2540 op = setFirstItem(operands);
2541 if ((immdGet(op, &stride) == NULL) || ((op = setNextItem(operands)) == NULL))
2543 fprintf (stderr,
2544 "*** internal error: inSequence peephole restriction"
2545 " malformed: %s\n", cmdLine);
2546 return FALSE;
2549 for (seq = LONG_MIN; op; op = setNextItem(operands))
2551 if ((immdGet(op, &val) == NULL) || ((seq != LONG_MIN) && (val != seq+stride)))
2553 deleteSet(&operands);
2554 return FALSE;
2556 seq = val;
2559 deleteSet(&operands);
2560 return TRUE;
2563 /*-----------------------------------------------------------------*/
2564 /* isPort - return true if port name matches one of args */
2565 /*-----------------------------------------------------------------*/
2566 FBYNAME (isPort)
2568 const char *name;
2569 bool ret = false;
2571 set *operands = setFromConditionArgs(cmdLine, vars);
2573 if (!operands)
2575 fprintf(stderr,
2576 "*** internal error: isPort peephole restriction"
2577 " malformed: %s\n", cmdLine);
2578 return false;
2581 while (name = setFirstItem(operands))
2583 deleteSetItem(&operands, (void *)name);
2585 if (strcmp(port->target, name) == 0)
2587 ret = true;
2588 break;
2592 deleteSet(&operands);
2594 return ret;
2597 /*-----------------------------------------------------------------*/
2598 /* notInJumpTable - check that we are not in a jump table */
2599 /*-----------------------------------------------------------------*/
2600 FBYNAME (notInJumpTable)
2602 return (currPl->ic && currPl->ic->op != JUMPTABLE);
2605 static const struct ftab
2607 char *fname;
2608 int (*func) (hTab *, lineNode *, lineNode *, lineNode *, char *);
2610 ftab[] = // sorted on the number of times used
2611 { // in the peephole rules on 2010-06-12
2613 "labelRefCount", labelRefCount // 161
2616 "notVolatile", notVolatile // 118
2619 "notUsed", notUsed // 96
2622 "labelRefCountChange", labelRefCountChange // 86
2625 "labelInRange", labelInRange // 51
2628 "notSame", notSame // 28
2631 "operandsNotRelated", operandsNotRelated // 28
2634 "same", same // z88dk z80
2637 "labelJTInRange", labelJTInRange // 13
2640 "24bitMode", flat24bitMode // 9
2643 "canAssign", canAssign // 8
2646 "inSequence", inSequence // z88dk z80
2649 "optimizeFor", optimizeFor
2652 "optimizeReturn", optimizeReturn // ? just a guess
2655 "notUsedFrom", notUsedFrom // ? just a guess
2658 "labelIsReturnOnly", labelIsReturnOnly // 6
2661 "operandsLiteral", operandsLiteral // 6
2664 "operandsLitOrSym", operandsLitOrSym
2667 "removeParentheses", removeParentheses
2670 "labelIsUncondJump", labelIsUncondJump // 4
2673 "deadMove", deadMove // 2
2676 "useAcallAjmp", useAcallAjmp // 2
2679 "xramMovcOption", xramMovcOption // 2
2682 "okToRemoveSLOC", okToRemoveSLOC // 0
2685 "immdInRange", immdInRange
2688 "notSimilar", notSimilar
2691 "symmParmStack", symmParmStack
2694 "isPort", isPort
2697 "canJoinRegs", canJoinRegs
2700 "canSplitReg", canSplitReg
2703 "unusedReg", unusedReg
2706 "newLabel", newLabel
2709 "notInJumpTable", notInJumpTable
2713 /*-----------------------------------------------------------------*/
2714 /* callFuncByName - calls a function as defined in the table */
2715 /*-----------------------------------------------------------------*/
2716 static int
2717 callFuncByName (char *fname,
2718 hTab *vars,
2719 lineNode *currPl, /* first source line matched */
2720 lineNode *endPl, /* last source line matched */
2721 lineNode *head)
2723 int i;
2724 char *cmdCopy, *funcName, *funcArgs, *cmdTerm;
2725 char c;
2726 int rc;
2728 /* Isolate the function name part (we are passed the full condition
2729 * string including arguments)
2731 cmdTerm = cmdCopy = Safe_strdup(fname);
2735 funcArgs = funcName = cmdTerm;
2736 while ((c = *funcArgs) && c != ' ' && c != '\t' && c != '(')
2737 funcArgs++;
2738 *funcArgs = '\0'; /* terminate the function name */
2739 if (c)
2740 funcArgs++;
2742 /* Find the start of the arguments */
2743 if (c == ' ' || c == '\t')
2744 while ((c = *funcArgs) && (c == ' ' || c == '\t'))
2745 funcArgs++;
2747 /* If the arguments started with an opening parenthesis, */
2748 /* use the closing parenthesis for the end of the */
2749 /* arguments and look for the start of another condition */
2750 /* that can optionally follow. If there was no opening */
2751 /* parethesis, then everything that follows are arguments */
2752 /* and there can be no additional conditions. */
2753 if (c == '(')
2756 int num_parenthesis = 0;
2757 cmdTerm = funcArgs;
2759 while ((c = *cmdTerm) && (c != ')' || num_parenthesis))
2761 if (c == '(')
2762 num_parenthesis++;
2763 else if (c == ')')
2764 num_parenthesis--;
2765 cmdTerm++;
2767 *cmdTerm = '\0'; /* terminate the arguments */
2768 if (c == ')')
2770 cmdTerm++;
2771 while ((c = *cmdTerm) && (c == ' ' || c == '\t' || c == ','))
2772 cmdTerm++;
2773 if (!*cmdTerm)
2774 cmdTerm = NULL;
2776 else
2777 cmdTerm = NULL; /* closing parenthesis missing */
2779 else
2780 cmdTerm = NULL;
2782 if (!*funcArgs)
2783 funcArgs = NULL;
2785 rc = -1;
2786 for (i = 0; i < ((sizeof (ftab)) / (sizeof (struct ftab))); i++)
2788 if (strcmp (ftab[i].fname, funcName) == 0)
2790 rc = (*ftab[i].func) (vars, currPl, endPl, head, funcArgs);
2791 break;
2795 if (rc == -1)
2797 fprintf (stderr,
2798 "could not find named function \"%s\" in "
2799 "peephole function table\n",
2800 funcName);
2801 // If the function couldn't be found, let's assume it's
2802 // a bad rule and refuse it.
2803 rc = FALSE;
2804 break;
2807 while (rc && cmdTerm);
2809 Safe_free(cmdCopy);
2811 return rc;
2814 /*-----------------------------------------------------------------*/
2815 /* newPeepRule - creates a new peeprule and attach it to the root */
2816 /*-----------------------------------------------------------------*/
2817 static peepRule *
2818 newPeepRule (lineNode * match,
2819 lineNode * replace,
2820 char *cond,
2821 int restart,
2822 int barrier)
2824 peepRule *pr;
2826 pr = Safe_alloc ( sizeof (peepRule));
2827 pr->match = match;
2828 pr->replace = replace;
2829 pr->restart = restart;
2830 pr->barrier = barrier;
2832 if (cond && *cond)
2834 pr->cond = Safe_strdup (cond);
2836 else
2837 pr->cond = NULL;
2839 pr->vars = newHashTable (16);
2841 /* if root is empty */
2842 if (!rootRules)
2843 rootRules = currRule = pr;
2844 else
2845 currRule = currRule->next = pr;
2847 return pr;
2850 #define SKIP_SPACE(x,y) { while (*x && (ISCHARSPACE(*x) || *x == '\n')) x++; \
2851 if (!*x) { fprintf(stderr,y); return ; } }
2853 #define EXPECT_STR(x,y,z) { while (*x && strncmp(x,y,strlen(y))) x++ ; \
2854 if (!*x) { fprintf(stderr,z); return ; } }
2855 #define EXPECT_CHR(x,y,z) { while (*x && *x != y) x++ ; \
2856 if (!*x) { fprintf(stderr,z); return ; } }
2858 /*-----------------------------------------------------------------*/
2859 /* getPeepLine - parses the peep lines */
2860 /*-----------------------------------------------------------------*/
2861 static void
2862 getPeepLine (lineNode ** head, const char **bpp)
2864 char lines[MAX_PATTERN_LEN];
2865 char *lp;
2866 int isComment;
2868 lineNode *currL = NULL;
2869 const char *bp = *bpp;
2870 while (1)
2873 if (!*bp)
2875 fprintf (stderr, "unexpected end of match pattern\n");
2876 return;
2879 if (*bp == '\n')
2881 bp++;
2882 while (ISCHARSPACE (*bp) ||
2883 *bp == '\n')
2884 bp++;
2887 if (*bp == '}')
2889 bp++;
2890 break;
2893 /* read till end of line */
2894 lp = lines;
2895 while ((*bp != '\n' && *bp != '}') && *bp)
2896 *lp++ = *bp++;
2897 *lp = '\0';
2899 lp = lines;
2900 while (*lp && ISCHARSPACE(*lp))
2901 lp++;
2902 isComment = (*lp == ';');
2904 if (!isComment || (isComment && !options.noPeepComments))
2906 const char *dummy1;
2907 int dummy2;
2909 if (!currL)
2910 *head = currL = newLineNode (lines);
2911 else
2912 currL = connectLine (currL, newLineNode (lines));
2913 currL->isComment = isComment;
2914 currL->isLabel = isLabelDefinition (currL->line, &dummy1, &dummy2,
2915 TRUE);
2920 *bpp = bp;
2923 /*-----------------------------------------------------------------*/
2924 /* readRules - reads the rules from a string buffer */
2925 /*-----------------------------------------------------------------*/
2926 static void
2927 readRules (const char *bp)
2929 char restart = 0, barrier = 0;
2930 char lines[MAX_PATTERN_LEN];
2931 size_t safetycounter;
2932 char *lp;
2933 const char *rp;
2934 lineNode *match;
2935 lineNode *replace;
2936 lineNode *currL = NULL;
2938 if (!bp)
2939 return;
2940 top:
2941 restart = 0;
2942 barrier = 0;
2944 /* look for the token "replace" that is the
2945 start of a rule */
2946 while (*bp && strncmp (bp, "replace", 7))
2948 if (!strncmp (bp, "barrier", 7))
2949 barrier = 1;
2950 bp++;
2953 /* if not found */
2954 if (!*bp)
2955 return;
2957 /* then look for either "restart" or '{' */
2958 while (strncmp (bp, "restart", 7) && *bp != '{' && bp)
2959 bp++;
2961 /* not found */
2962 if (!*bp)
2964 fprintf (stderr, "expected 'restart' or '{'\n");
2965 return;
2968 /* if brace */
2969 if (*bp == '{')
2970 bp++;
2971 else
2972 { /* must be restart */
2973 restart++;
2974 bp += strlen ("restart");
2975 /* look for '{' */
2976 EXPECT_CHR (bp, '{', "expected '{'\n");
2977 bp++;
2980 /* skip thru all the blank space */
2981 SKIP_SPACE (bp, "unexpected end of rule\n");
2983 match = replace = currL = NULL;
2984 /* we are the start of a rule */
2985 getPeepLine (&match, &bp);
2987 /* now look for by */
2988 EXPECT_STR (bp, "by", "expected 'by'\n");
2990 /* then look for a '{' */
2991 EXPECT_CHR (bp, '{', "expected '{'\n");
2992 bp++;
2994 /* save char position (needed for generating error msg) */
2995 rp = bp;
2997 SKIP_SPACE (bp, "unexpected end of rule\n");
2998 getPeepLine (&replace, &bp);
3000 /* look for a 'if' */
3001 while ((ISCHARSPACE (*bp) || *bp == '\n' || (*bp == '/' && *(bp+1) == '/')) && *bp)
3003 ++bp;
3004 if (*bp == '/') while (*bp && *bp != '\n') ++bp;
3007 if (strncmp (bp, "if", 2) == 0)
3009 bp += 2;
3010 while ((ISCHARSPACE (*bp) || *bp == '\n' || (*bp == '/' && *(bp+1) == '/')) && *bp)
3012 bp++;
3013 if (*bp == '/')
3014 while (*bp && *bp != '\n')
3015 bp++;
3017 if (!*bp)
3019 fprintf (stderr, "expected condition name\n");
3020 return;
3023 /* look for the condition */
3024 lp = lines;
3025 for (safetycounter = 0; *bp && (*bp != '\n'); safetycounter++)
3027 wassertl(safetycounter < MAX_PATTERN_LEN, "Peephole line too long.\n");
3028 *lp++ = *bp++;
3030 *lp = '\0';
3032 newPeepRule (match, replace, lines, restart, barrier);
3034 else
3036 if (*bp && strncmp (bp, "replace", 7) && strncmp (bp, "barrier", 7))
3038 /* not the start of a new peeprule, so "if" should be here */
3040 char strbuff[1000];
3041 char *cp;
3043 /* go to the start of the line following "{" of the "by" token */
3044 while (*rp && (*rp == '\n'))
3045 rp++;
3047 /* copy text of rule starting with line after "by {" */
3048 cp = strbuff;
3049 while (*rp && (rp < bp) && ((cp - strbuff) < sizeof(strbuff)))
3050 *cp++ = *rp++;
3052 /* and now the rest of the line */
3053 while (*rp && (*rp != '\n') && ((cp - strbuff) < sizeof(strbuff)))
3054 *cp++ = *rp++;
3056 *cp = '\0';
3057 fprintf (stderr, "%s\nexpected '} if ...'\n", strbuff);
3058 return;
3060 newPeepRule (match, replace, NULL, restart, barrier);
3062 goto top;
3066 /*-----------------------------------------------------------------*/
3067 /* keyForVar - returns the numeric key for a var */
3068 /*-----------------------------------------------------------------*/
3069 static int
3070 keyForVar (const char *d)
3072 int i = 0;
3074 while (ISCHARDIGIT (*d))
3076 i *= 10;
3077 i += (*d++ - '0');
3080 return i;
3083 /*-----------------------------------------------------------------*/
3084 /* bindVar - binds a value to a variable in the given hashtable */
3085 /*-----------------------------------------------------------------*/
3086 static void
3087 bindVar (int key, char **s, hTab ** vtab)
3089 char vval[MAX_PATTERN_LEN];
3090 char *vvx;
3091 char *vv = vval;
3093 /* first get the value of the variable */
3094 vvx = *s;
3095 /* the value is ended by a ',' or space or newline or null or ) */
3096 while (*vvx &&
3097 *vvx != ',' &&
3098 !ISCHARSPACE (*vvx) &&
3099 *vvx != '\n' &&
3100 *vvx != ':' &&
3101 *vvx != ';' &&
3102 *vvx != ')')
3104 char ubb = 0;
3105 /* if we find a '(' then we need to balance it */
3106 if (*vvx == '(')
3108 ubb++;
3109 while (ubb)
3111 *vv++ = *vvx++;
3112 if (*vvx == '(')
3113 ubb++;
3114 if (*vvx == ')')
3115 ubb--;
3117 // include the trailing ')'
3118 *vv++ = *vvx++;
3120 else
3121 *vv++ = *vvx++;
3123 *s = vvx;
3124 *vv = '\0';
3125 /* got value */
3126 vvx = traceAlloc (&_G.values, Safe_strdup(vval));
3128 hTabAddItem (vtab, key, vvx);
3131 /*-----------------------------------------------------------------*/
3132 /* matchLine - matches one line */
3133 /*-----------------------------------------------------------------*/
3134 static bool
3135 matchLine (char *s, const char *d, hTab ** vars)
3137 if (!s || !(*s))
3138 return FALSE;
3140 /* skip leading white spaces */
3141 while (ISCHARSPACE (*s))
3142 s++;
3143 while (ISCHARSPACE (*d))
3144 d++;
3146 while (*s && *d)
3148 /* skip white space in both */
3149 while (ISCHARSPACE(*s))
3150 s++;
3151 if(*s==';') break;
3153 while (ISCHARSPACE(*d))
3154 d++;
3156 /* if the destination is a var */
3157 if (*d == '%' && ISCHARDIGIT (*(d + 1)) && vars)
3159 const char *v = hTabItemWithKey (*vars, keyForVar (d + 1));
3160 /* if the variable is already bound
3161 then it MUST match with dest */
3162 if (v)
3164 while (*v)
3165 if (*v++ != *s++)
3166 return FALSE;
3168 else
3169 /* variable not bound we need to bind it */
3170 bindVar (keyForVar (d + 1), &s, vars);
3172 /* in either case go past the variable */
3173 d++;
3174 while (ISCHARDIGIT (*d))
3175 d++;
3177 else if (*s == ',' && *d == ',') /* Allow comma to match comma */
3179 s++, d++;
3181 else if (*s && *d) /* they should be an exact match otherwise */
3183 if (*s++ != *d++)
3184 return FALSE;
3188 /* skip trailing whitespaces */
3189 if (*s)
3190 while (ISCHARSPACE (*s))
3191 s++;
3193 /* skip trailing comments as well*/
3194 if(*s==';')
3195 while (*s)
3196 s++;
3198 if (*d)
3199 while (ISCHARSPACE (*d))
3200 d++;
3202 /* after all this if only one of them
3203 has something left over then no match */
3204 if (*s || *d)
3205 return FALSE;
3207 return TRUE;
3210 /*-----------------------------------------------------------------*/
3211 /* matchRule - matches a all the rule lines */
3212 /*-----------------------------------------------------------------*/
3213 static bool
3214 matchRule (lineNode * pl,
3215 lineNode ** mtail,
3216 peepRule * pr,
3217 lineNode * head)
3219 lineNode *spl; /* source pl */
3220 lineNode *rpl; /* rule peep line */
3222 /* setToNull((void *) &pr->vars); */
3223 /* pr->vars = newHashTable(100); */
3225 /* for all the lines defined in the rule */
3226 rpl = pr->match;
3227 spl = pl;
3228 while (spl && rpl)
3231 /* if the source line starts with a ';' then
3232 comment line don't process or the source line
3233 contains == . debugger information skip it */
3234 if (spl->line &&
3235 (*spl->line == ';' || spl->isDebug))
3237 spl = spl->next;
3238 continue;
3241 if (!matchLine (spl->line, rpl->line, &pr->vars))
3242 return FALSE;
3244 rpl = rpl->next;
3245 if (rpl)
3246 spl = spl->next;
3249 /* if rules ended */
3250 if (!rpl)
3252 /* if this rule has additional conditions */
3253 if (pr->cond)
3255 /* constraints which uses variables as destination container
3256 requires to vars table to be defined */
3257 if (!pr->vars)
3258 pr->vars = newHashTable (128);
3260 if (callFuncByName (pr->cond, pr->vars, pl, spl, head))
3262 *mtail = spl;
3263 return TRUE;
3265 else
3266 return FALSE;
3268 else
3270 *mtail = spl;
3271 return TRUE;
3274 else
3275 return FALSE;
3278 static void
3279 reassociate_ic_down (lineNode *shead, lineNode *stail,
3280 lineNode *rhead, lineNode *rtail)
3282 lineNode *csl; /* current source line */
3283 lineNode *crl; /* current replacement line */
3285 csl = shead;
3286 crl = rhead;
3287 while (1)
3289 /* skip over any comments */
3290 while (csl!=stail->next && csl->isComment)
3291 csl = csl->next;
3292 while (crl!=rtail->next && crl->isComment)
3293 crl = crl->next;
3295 /* quit if we reach the end */
3296 if ((csl==stail->next) || (crl==rtail->next) || crl->ic)
3297 break;
3299 if (matchLine(csl->line,crl->line,NULL))
3301 crl->ic = csl->ic;
3302 csl = csl->next;
3303 crl = crl->next;
3305 else
3306 break;
3310 static void
3311 reassociate_ic_up (lineNode *shead, lineNode *stail,
3312 lineNode *rhead, lineNode *rtail)
3314 lineNode *csl; /* current source line */
3315 lineNode *crl; /* current replacement line */
3317 csl = stail;
3318 crl = rtail;
3319 while (1)
3321 /* skip over any comments */
3322 while (csl!=shead->prev && csl->isComment)
3323 csl = csl->prev;
3324 while (crl!=rhead->prev && crl->isComment)
3325 crl = crl->prev;
3327 /* quit if we reach the end */
3328 if ((csl==shead->prev) || (crl==rhead->prev) || crl->ic)
3329 break;
3331 if (matchLine(csl->line,crl->line,NULL))
3333 crl->ic = csl->ic;
3334 csl = csl->prev;
3335 crl = crl->prev;
3337 else
3338 break;
3342 /*------------------------------------------------------------------*/
3343 /* reassociate_ic - reassociate replacement lines with origin iCode */
3344 /*------------------------------------------------------------------*/
3345 static void
3346 reassociate_ic (lineNode *shead, lineNode *stail,
3347 lineNode *rhead, lineNode *rtail)
3349 lineNode *csl; /* current source line */
3350 lineNode *crl; /* current replacement line */
3351 bool single_iCode;
3352 iCode *ic;
3354 /* Check to see if all the source lines (excluding comments) came
3355 ** for the same iCode
3357 ic = NULL;
3358 for (csl=shead;csl!=stail->next;csl=csl->next)
3359 if (csl->ic && !csl->isComment)
3361 ic = csl->ic;
3362 break;
3364 single_iCode = (ic!=NULL);
3365 for (csl=shead;csl!=stail->next;csl=csl->next)
3366 if ((csl->ic != ic) && !csl->isComment)
3368 /* More than one iCode was found. However, if it's just the
3369 ** last line with the different iCode and it was not changed
3370 ** in the replacement, everything else must be the first iCode.
3372 if ((csl==stail) && matchLine (stail->line, rtail->line, NULL))
3374 rtail->ic = stail->ic;
3375 for (crl=rhead;crl!=rtail;crl=crl->next)
3376 crl->ic = ic;
3377 return;
3380 single_iCode = FALSE;
3381 break;
3384 /* If all of the source lines came from the same iCode, then so have
3385 ** all of the replacement lines too.
3387 if (single_iCode)
3389 for (crl=rhead;crl!=rtail->next;crl=crl->next)
3390 crl->ic = ic;
3391 return;
3394 /* The source lines span iCodes, so we may end up with replacement
3395 ** lines that we don't know which iCode(s) to associate with. Do the
3396 ** best we can by using the following strategies:
3397 ** 1) Start at the top and scan down. As long as the source line
3398 ** matches the replacement line, they have the same iCode.
3399 ** 2) Start at the bottom and scan up. As long as the source line
3400 ** matches the replacement line, they have the same iCode.
3401 ** 3) For any label in the source, look for a matching label in
3402 ** the replacement. If found, they have the same iCode. From
3403 ** these matching labels, scan down for additional matching
3404 ** lines; if found, they also have the same iCode.
3407 /* Strategy #1: Start at the top and scan down for matches
3409 reassociate_ic_down(shead,stail,rhead,rtail);
3411 /* Strategy #2: Start at the bottom and scan up for matches
3413 reassociate_ic_up(shead,stail,rhead,rtail);
3415 /* Strategy #3: Try to match labels
3417 csl = shead;
3418 while (1)
3420 /* skip over any comments */
3421 while (csl!=stail->next && csl->isComment)
3422 csl = csl->next;
3423 if (csl==stail->next)
3424 break;
3426 if (csl->isLabel)
3428 /* found a source line label; look for it in the replacement lines */
3429 crl = rhead;
3430 while (1)
3432 while (crl!=rtail->next && crl->isComment)
3433 crl = crl->next;
3434 if (crl==rtail->next)
3435 break;
3436 if (matchLine(csl->line, crl->line, NULL))
3438 reassociate_ic_down(csl,stail,crl,rtail);
3439 break;
3441 else
3442 crl = crl->next;
3445 csl = csl->next;
3448 /* Try to assign a meaningful iCode to any comment that is missing
3449 one. Since they are comments, it's ok to make mistakes; we are just
3450 trying to improve continuity to simplify other tests.
3452 ic = NULL;
3453 for (crl=rtail;crl!=rhead->prev;crl=crl->prev)
3455 if (!crl->ic && ic && crl->isComment)
3456 crl->ic = ic;
3457 ic = crl->ic;
3462 /*-----------------------------------------------------------------*/
3463 /* replaceRule - does replacement of a matching pattern */
3464 /*-----------------------------------------------------------------*/
3465 static void
3466 replaceRule (lineNode **shead, lineNode *stail, const peepRule *pr)
3468 lineNode *cl = NULL;
3469 lineNode *pl = NULL, *lhead = NULL;
3470 /* a long function name and long variable name can evaluate to
3471 4x max pattern length e.g. "mov dptr,((fie_var>>8)<<8)+fie_var" */
3472 char lb[MAX_PATTERN_LEN*4];
3473 char *lbp;
3474 lineNode *comment = NULL;
3476 /* collect all the comment lines in the source */
3477 for (cl = *shead; cl != stail; cl = cl->next)
3479 if (cl->line && (*cl->line == ';' || cl->isDebug))
3481 pl = (pl ? connectLine (pl, newLineNode (cl->line)) :
3482 (comment = newLineNode (cl->line)));
3483 pl->isDebug = cl->isDebug;
3484 pl->isComment = cl->isComment || (*cl->line == ';');
3487 cl = NULL;
3489 /* for all the lines in the replacement pattern do */
3490 for (pl = pr->replace; pl; pl = pl->next)
3492 char *v;
3493 char *l;
3494 lbp = lb;
3496 l = pl->line;
3498 while (*l)
3500 /* if the line contains a variable */
3501 if (*l == '%' && ISCHARDIGIT (*(l + 1)))
3503 v = hTabItemWithKey (pr->vars, keyForVar (l + 1));
3504 if (!v)
3506 fprintf (stderr, "used unbound variable in replacement\n");
3507 l++;
3508 continue;
3510 while (*v) {
3511 *lbp++ = *v++;
3513 l++;
3514 while (ISCHARDIGIT (*l)) {
3515 l++;
3517 continue;
3519 *lbp++ = *l++;
3522 *lbp = '\0';
3523 if (cl)
3524 cl = connectLine (cl, newLineNode (lb));
3525 else
3526 lhead = cl = newLineNode (lb);
3527 cl->isComment = pl->isComment;
3528 cl->isLabel = pl->isLabel;
3531 /* add the comments if any to the head of list */
3532 if (comment)
3534 lineNode *lc = comment;
3535 while (lc->next)
3536 lc = lc->next;
3537 if (lhead)
3539 lc->next = lhead;
3540 lhead->prev = lc;
3542 else
3543 cl = lc;
3544 lhead = comment;
3547 if (lhead)
3549 /* determine which iCodes the replacement lines relate to */
3550 reassociate_ic(*shead,stail,lhead,cl);
3552 /* now we need to connect / replace the original chain */
3553 /* if there is a prev then change it */
3554 if ((*shead)->prev)
3556 (*shead)->prev->next = lhead;
3557 lhead->prev = (*shead)->prev;
3559 *shead = lhead;
3560 /* now for the tail */
3561 if (stail && stail->next)
3563 stail->next->prev = cl;
3564 cl->next = stail->next;
3567 else
3569 /* the replacement is empty - delete the source lines */
3570 if ((*shead)->prev)
3571 (*shead)->prev->next = stail->next;
3572 if (stail->next)
3573 stail->next->prev = (*shead)->prev;
3574 *shead = stail->next;
3578 /* Returns TRUE if this line is a label definition.
3580 * If so, start will point to the start of the label name,
3581 * and len will be it's length.
3583 bool
3584 isLabelDefinition (const char *line, const char **start, int *len, bool isPeepRule)
3586 const char *cp = line;
3588 /* This line is a label if it consists of:
3589 * [optional whitespace] followed by identifier chars
3590 * (alnum | $ | _ ) followed by a colon.
3593 while (*cp && ISCHARSPACE (*cp))
3595 cp++;
3598 if (!*cp)
3600 return FALSE;
3603 *start = cp;
3605 while (ISCHARALNUM (*cp) || (*cp == '$') || (*cp == '_') ||
3606 (isPeepRule && (*cp == '%')))
3608 cp++;
3611 if ((cp == *start) || (*cp != ':'))
3613 return FALSE;
3616 *len = (cp - (*start));
3617 return TRUE;
3620 /* Not perfect, will not find all references yet.
3621 Will however find references in call on Z80, which is sufficient to fix #2970351. */
3622 bool
3623 isLabelReference (const char *line, const char **start, int *len)
3625 const char *s, *e;
3626 if (!TARGET_Z80_LIKE && !TARGET_IS_STM8 && !TARGET_PDK_LIKE)
3627 return FALSE;
3629 s = line;
3630 while (ISCHARSPACE (*s))
3631 ++s;
3633 if(strncmp(s, "call", 4))
3634 return FALSE;
3635 s += 4;
3637 while (ISCHARSPACE (*s))
3638 ++s;
3640 /* Skip condition in conditional call */
3641 if (strchr(s, ','))
3642 s = strchr(s, ',') + 1;
3644 e = s, *len = 0;
3645 while(*e && !ISCHARSPACE (*e) && *e != ';')
3646 ++e, ++(*len);
3648 *start = s;
3650 return TRUE;
3653 /* Quick & dirty string hash function. */
3654 static int
3655 hashSymbolName (const char *name)
3657 int hash = 0;
3659 while (*name)
3661 hash = ((unsigned)hash << 6) ^ *name;
3662 name++;
3665 if (hash < 0)
3667 hash = -hash;
3670 return hash % HTAB_SIZE;
3673 /* Build a hash of all labels in the passed set of lines
3674 * and how many times they are referenced.
3676 static void
3677 buildLabelRefCountHash (lineNode *head)
3679 lineNode *line;
3680 const char *label;
3681 int labelLen;
3682 int i;
3684 assert (labelHash == NULL);
3685 labelHash = newHashTable (HTAB_SIZE);
3687 /* First pass: locate all the labels. */
3688 for (line = head; line; line = line->next)
3690 bool ref = FALSE;
3691 /* run isLabelDefinition to:
3692 - look for labels in inline assembler
3693 - calculate labelLen
3695 if ((line->isLabel || line->isInline) && isLabelDefinition (line->line, &label, &labelLen, FALSE) ||
3696 (ref = TRUE) && isLabelReference (line->line, &label, &labelLen))
3698 labelHashEntry *entry, *e;
3700 assert (labelLen <= SDCC_NAME_MAX);
3702 entry = traceAlloc (&_G.labels, Safe_alloc(sizeof (labelHashEntry)));
3704 memcpy (entry->name, label, labelLen);
3705 entry->name[labelLen] = 0;
3706 entry->refCount = -1;
3708 for (e = hTabFirstItemWK (labelHash, hashSymbolName (entry->name)); e; e = hTabNextItemWK (labelHash))
3709 if (!strcmp (entry->name, e->name))
3710 goto c;
3712 /* Assume function entry points are referenced somewhere, */
3713 /* even if we can't find a reference (might be from outside */
3714 /* the function) */
3715 if (line->ic && (line->ic->op == FUNCTION) || ref)
3716 entry->refCount++;
3718 hTabAddItem (&labelHash, hashSymbolName (entry->name), entry);
3724 /* Second pass: for each line, note all the referenced labels. */
3725 /* This is ugly, O(N^2) stuff. Optimizations welcome... */
3726 for (line = head; line; line = line->next)
3728 if (line->isComment)
3729 continue;
3731 /* Padauk skip instructions */
3732 if (TARGET_PDK_LIKE && !line->isInline &&
3733 (!strncmp(line->line, "ceqsn", 5) || !strncmp(line->line, "cneqsn", 6) ||
3734 !strncmp(line->line, "t0sn", 4) || !strncmp(line->line, "t1sn", 4) ||
3735 !strncmp(line->line, "izsn", 4) || !strncmp(line->line, "dzsn", 4)))
3737 const lineNode *l = line;
3738 // Skip over following inst.
3740 l = l->next;
3741 while(l && (l->isComment || l->isDebug || l->isLabel));
3742 if (l)
3744 l = l->next;
3745 while(l && (l->isComment || l->isDebug));
3747 if (l && l->isLabel && isLabelDefinition (l->line, &label, &labelLen, false))
3749 char name[SDCC_NAME_MAX];
3750 strcpy(name, label);
3751 name[labelLen] = 0;
3753 labelHashEntry *e;
3754 for (e = hTabFirstItemWK (labelHash, hashSymbolName (name)); e; e = hTabNextItemWK (labelHash))
3755 if (!strcmp (name, e->name))
3756 break;
3758 if (e)
3759 e->refCount++;
3761 else
3762 werror (E_NO_SKIP_TARGET, line->line);
3765 for (i = 0; i < HTAB_SIZE; i++)
3767 labelHashEntry *thisEntry;
3769 thisEntry = hTabFirstItemWK (labelHash, i);
3771 while (thisEntry)
3773 const char *s;
3774 if ((s = strstr (line->line, thisEntry->name)) && !ISCHARALNUM (*(s + strlen (thisEntry->name))) && (s == line->line || !ISCHARALNUM (*(s - 1))))
3775 thisEntry->refCount++;
3777 thisEntry = hTabNextItemWK (labelHash);
3782 #if 0
3783 /* Spew the contents of the table. Debugging fun only. */
3784 for (i = 0; i < HTAB_SIZE; i++)
3786 labelHashEntry *thisEntry;
3788 thisEntry = hTabFirstItemWK (labelHash, i);
3790 while (thisEntry)
3792 fprintf (stderr, "label: %s (%p) ref %d\n",
3793 thisEntry->name, thisEntry, thisEntry->refCount);
3794 thisEntry = hTabNextItemWK (labelHash);
3797 #endif
3800 /* How does this work?
3801 peepHole
3802 For each rule,
3803 For each line,
3804 Try to match
3805 If it matches,
3806 replace and restart.
3808 matchRule
3809 matchLine
3811 Where is stuff allocated?
3815 /*-----------------------------------------------------------------*/
3816 /* peepHole - matches & substitutes rules */
3817 /*-----------------------------------------------------------------*/
3818 void
3819 peepHole (lineNode ** pls)
3821 lineNode *spl;
3822 peepRule *pr;
3823 lineNode *mtail = NULL;
3824 bool restart, replaced;
3825 unsigned long rule_application_counter = 0ul;
3827 #if !OPT_DISABLE_PIC14 || !OPT_DISABLE_PIC16
3828 /* The PIC port uses a different peep hole optimizer based on "pCode" */
3829 if (TARGET_PIC_LIKE)
3830 return;
3831 #endif
3833 assert(labelHash == NULL);
3837 restart = FALSE;
3839 /* for all rules */
3840 for (pr = rootRules; pr; pr = pr->next)
3842 if (restart && pr->barrier)
3843 break;
3845 for (spl = *pls; spl; spl = replaced ? spl : spl->next)
3847 replaced = FALSE;
3849 // Break out of an infinite loop of rule applications.
3850 if (rule_application_counter > 200000ul)
3852 werror (W_PEEPHOLE_RULE_LIMIT);
3853 goto end;
3856 /* if inline assembler then no peep hole */
3857 if (spl->isInline)
3858 continue;
3860 /* don't waste time starting a match on debug symbol
3861 ** or comment */
3862 if (spl->isDebug || spl->isComment || *(spl->line)==';')
3863 continue;
3865 mtail = NULL;
3867 /* Tidy up any data stored in the hTab */
3869 /* if it matches */
3870 if (matchRule (spl, &mtail, pr, *pls))
3872 rule_application_counter++;
3874 /* restart at the replaced line */
3875 replaced = TRUE;
3877 /* then replace */
3878 if (spl == *pls)
3880 replaceRule (pls, mtail, pr);
3881 spl = *pls;
3883 else
3884 replaceRule (&spl, mtail, pr);
3886 /* if restart rule type then
3887 start at the top again */
3888 if (pr->restart)
3890 restart = TRUE;
3894 if (pr->vars)
3896 hTabDeleteAll (pr->vars);
3897 Safe_free (pr->vars);
3898 pr->vars = NULL;
3901 freeTrace (&_G.values);
3904 } while (restart == TRUE);
3906 end:
3907 if (labelHash)
3909 hTabDeleteAll (labelHash);
3910 freeTrace (&_G.labels);
3912 labelHash = NULL;
3916 /*-----------------------------------------------------------------*/
3917 /* readFileIntoBuffer - reads a file into a string buffer */
3918 /*-----------------------------------------------------------------*/
3919 static char *
3920 readFileIntoBuffer (char *fname)
3922 FILE *f;
3923 char *rs = NULL;
3924 int nch = 0;
3925 int ch;
3926 char lb[MAX_PATTERN_LEN];
3928 if (!(f = fopen (fname, "r")))
3930 fprintf (stderr, "cannot open peep rule file\n");
3931 return NULL;
3934 while ((ch = fgetc (f)) != EOF)
3936 lb[nch++] = ch;
3938 /* if we maxed out our local buffer */
3939 if (nch >= (MAX_PATTERN_LEN - 2))
3941 lb[nch] = '\0';
3942 /* copy it into allocated buffer */
3943 if (rs)
3945 rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1);
3946 strncatz (rs, lb, strlen (rs) + strlen (lb) + 1);
3948 else
3950 rs = Safe_strdup (lb);
3952 nch = 0;
3955 fclose (f);
3957 /* if some characters left over */
3958 if (nch)
3960 lb[nch] = '\0';
3961 /* copy it into allocated buffer */
3962 if (rs)
3964 rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1);
3965 strncatz (rs, lb, strlen (rs) + strlen (lb) + 1);
3967 else
3969 rs = Safe_strdup (lb);
3972 return rs;
3975 /*-----------------------------------------------------------------*/
3976 /* initPeepHole - initialises the peep hole optimizer stuff */
3977 /*-----------------------------------------------------------------*/
3978 void
3979 initPeepHole (void)
3981 char *s;
3983 /* read in the default rules */
3984 if (!options.nopeep)
3986 readRules (port->peep.default_rules);
3989 /* if we have any additional file read it too */
3990 if (options.peep_file)
3992 readRules (s = readFileIntoBuffer (options.peep_file));
3993 setToNull ((void *) &s);
3994 /* override nopeep setting, default rules have not been read */
3995 options.nopeep = 0;
3998 #if !OPT_DISABLE_PIC14
3999 /* Convert the peep rules into pcode.
4000 NOTE: this is only support in the PIC port (at the moment)
4002 if (TARGET_IS_PIC14)
4003 peepRules2pCode (rootRules);
4004 #endif
4006 #if !OPT_DISABLE_PIC16
4007 /* Convert the peep rules into pcode.
4008 NOTE: this is only support in the PIC port (at the moment)
4009 and the PIC16 port (VR 030601)
4011 if (TARGET_IS_PIC16)
4012 pic16_peepRules2pCode (rootRules);
4014 #endif
4017 /*-----------------------------------------------------------------*/
4018 /* StrStr - case-insensitive strstr implementation */
4019 /*-----------------------------------------------------------------*/
4020 const char * StrStr (const char * str1, const char * str2)
4022 const char * cp = str1;
4023 const char * s1;
4024 const char * s2;
4026 if ( !*str2 )
4027 return str1;
4029 while (*cp)
4031 s1 = cp;
4032 s2 = str2;
4034 while ( *s1 && *s2 && !(tolower(*s1)-tolower(*s2)) )
4035 s1++, s2++;
4037 if (!*s2)
4038 return( cp );
4040 cp++;
4043 return (NULL) ;