1 #include "common.h"
2 #include "SDCCgen.h"
3 
4 #include "peep.h"
5 
6 #define NOTUSEDERROR() do {werror(E_INTERNAL_ERROR, __FILE__, __LINE__, "error in notUsed()");} while(0)
7 
8 // #define D(_s) { printf _s; fflush(stdout); }
9 #define D(_s)
10 
11 #define ISINST(l, i) (!STRNCASECMP((l), (i), sizeof(i) - 1) && (!(l)[sizeof(i) - 1] || isspace((unsigned char)((l)[sizeof(i) - 1]))))
12 
13 typedef enum
14 {
15   S4O_CONDJMP,
16   S4O_WR_OP,
17   S4O_RD_OP,
18   S4O_TERM,
19   S4O_VISITED,
20   S4O_ABORT,
21   S4O_CONTINUE
22 } S4O_RET;
23 
24 static struct
25 {
26   lineNode *head;
27 } _G;
28 
29 static bool
isReturned(const char * what)30 isReturned(const char *what)
31 {
32   symbol *sym;
33   sym_link *sym_lnk;
34   int size;
35   lineNode *l;
36 
37   l = _G.head;
38   do
39   {
40     l = l->next;
41   } while(l->isComment || l->ic == NULL || l->ic->op != FUNCTION);
42 
43   sym = OP_SYMBOL(IC_LEFT(l->ic));
44 
45   if(sym && IS_DECL(sym->type))
46     {
47       // Find size of return value.
48       specifier *spec;
49       if(sym->type->select.d.dcl_type != FUNCTION)
50         NOTUSEDERROR();
51       spec = &(sym->etype->select.s);
52       if(spec->noun == V_VOID)
53         size = 0;
54       else if(spec->noun == V_CHAR || spec->noun == V_BOOL)
55         size = 1;
56       else if(spec->noun == V_INT && !(spec->b_long))
57         size = 2;
58       else
59         size = 4;
60 
61       // Check for returned pointer.
62       sym_lnk = sym->type;
63       while (sym_lnk && !IS_PTR (sym_lnk))
64         sym_lnk = sym_lnk->next;
65       if(IS_PTR(sym_lnk))
66         size = 2;
67     }
68   else
69     {
70       NOTUSEDERROR();
71       return TRUE;
72     }
73 
74   switch(*what)
75     {
76     case 'a':
77       return(size == 1 || size == 2);
78     case 'p':
79       return(size == 2);
80     default:
81       return false;
82     }
83 }
84 
85 /*-----------------------------------------------------------------*/
86 /* incLabelJmpToCount - increment counter "jmpToCount" in entry    */
87 /* of the list labelHash                                           */
88 /*-----------------------------------------------------------------*/
89 static bool
incLabelJmpToCount(const char * label)90 incLabelJmpToCount (const char *label)
91 {
92   labelHashEntry *entry;
93 
94   entry = getLabelRef (label, _G.head);
95   if (!entry)
96     return FALSE;
97   entry->jmpToCount++;
98   return TRUE;
99 }
100 
101 /*-----------------------------------------------------------------*/
102 /* findLabel -                                                     */
103 /* 1. extracts label in the opcode pl                              */
104 /* 2. increment "label jump-to count" in labelHash                 */
105 /* 3. search lineNode with label definition and return it          */
106 /*-----------------------------------------------------------------*/
107 static lineNode *
findLabel(const lineNode * pl)108 findLabel (const lineNode *pl)
109 {
110   char *p;
111   lineNode *cpl;
112 
113   /* 1. extract label in opcode */
114 
115   /* In each jump the label is at the end */
116   p = strlen (pl->line) - 1 + pl->line;
117 
118   /* Skip trailing whitespace */
119   while(isspace(*p))
120     p--;
121 
122   /* scan backward until space or ',' */
123   for (; p > pl->line; p--)
124     if (isspace(*p) || *p == ',')
125       break;
126 
127   /* sanity check */
128   if (p == pl->line)
129     {
130       NOTUSEDERROR();
131       return NULL;
132     }
133 
134   /* skip ',' resp. '\t' */
135   ++p;
136 
137   /* 2. increment "label jump-to count" */
138   if (!incLabelJmpToCount (p))
139     return NULL;
140 
141   /* 3. search lineNode with label definition and return it */
142   for (cpl = _G.head; cpl; cpl = cpl->next)
143     {
144       if (   cpl->isLabel
145           && strncmp (p, cpl->line, strlen(p)) == 0)
146         {
147           return cpl;
148         }
149     }
150   return NULL;
151 }
152 
153 /* Check if reading arg implies reading what. */
argIs(const char * arg,const char * what)154 static bool argIs(const char *arg, const char *what)
155 {
156   if (arg == NULL || strlen (arg) == 0)
157     return false;
158 
159   while (isblank ((unsigned char)(arg[0])))
160     arg++;
161 
162   if (arg[0] == ',')
163     arg++;
164 
165   while (isblank ((unsigned char)(arg[0])))
166     arg++;
167 
168   return !strncmp(arg, what, strlen(what)) &&
169     (!arg[strlen(what)] || isspace((unsigned char)(arg[strlen(what)])) || arg[strlen(what)] == ',');
170 }
171 
172 static bool
stm8MightReadFlag(const lineNode * pl,const char * what)173 stm8MightReadFlag(const lineNode *pl, const char *what)
174 {
175   if (ISINST (pl->line, "push") && argIs (pl->line + 4, "af") || ISINST (pl->line, "pushaf"))
176     return true;
177 
178   if (ISINST (pl->line, "t0sn") || ISINST (pl->line, "t1sn"))
179     return argIs(strchr (pl->line, ','), what);
180 
181   if(ISINST (pl->line, "addc") ||
182     ISINST (pl->line, "subc"))
183     return !strcmp(what, "c");
184 
185   return false;
186 }
187 
188 static bool
pdkMightRead(const lineNode * pl,const char * what)189 pdkMightRead(const lineNode *pl, const char *what)
190 {
191 //printf("pdkMightRead() for |%s|%s|\n", pl->line, what);
192   if (!strcmp(what, "z") || !strcmp(what, "c") || !strcmp(what, "ac") || !strcmp(what, "ov"))
193     return (stm8MightReadFlag(pl, what));
194   else if (strcmp(what, "a") && strcmp(what, "p"))
195     return true;
196 
197   // Instructions that never read anything.
198   if (ISINST(pl->line, "engint") ||
199     ISINST(pl->line, "disgint") ||
200     ISINST(pl->line, "nop") ||
201     ISINST(pl->line, "pop") || ISINST(pl->line, "popaf") ||
202     ISINST(pl->line, "stopsys") ||
203     ISINST(pl->line, "wdreset"))
204     return false;
205 
206   if (ISINST(pl->line, "ret") && strchr(pl->line + 2, '#') && !strcmp(what, "a"))
207     return false;
208   if (ISINST(pl->line, "ret"))
209     return isReturned(what);
210 
211   if (ISINST(pl->line, "mov"))
212     return argIs (strchr (pl->line, ','), what);
213 
214   if (ISINST (pl->line, "push") && argIs (pl->line + 4, "af") || ISINST (pl->line, "pushaf"))
215     return !strcmp(what, "a");
216 
217   // Two-operand instructions that read both operands
218   if (ISINST (pl->line, "add") ||
219     ISINST (pl->line, "and") ||
220     ISINST (pl->line, "or") ||
221     ISINST (pl->line, "sub") ||
222     ISINST (pl->line, "xor"))
223     return argIs (pl->line + 3, what) || argIs (strchr (pl->line, ','), what);
224   if (ISINST(pl->line, "idxm"))
225     return argIs (pl->line + 4, what) || argIs (strchr (pl->line, ','), what);
226   if (ISINST (pl->line, "ceqsn") ||
227     ISINST (pl->line, "cneqsn"))
228     return argIs (pl->line + 6, what) || argIs (strchr (pl->line, ','), what);
229 
230   // One-operand instructions
231   if (ISINST (pl->line, "neg") ||
232     ISINST (pl->line, "not") ||
233     ISINST (pl->line, "sl") ||
234     ISINST (pl->line, "slc") ||
235     ISINST (pl->line, "sr") ||
236     ISINST (pl->line, "src"))
237     return argIs (pl->line + 3, what);
238   if (ISINST (pl->line, "dzsn") ||
239     ISINST (pl->line, "izsn") ||
240     ISINST (pl->line, "pcadd") ||
241     ISINST (pl->line, "stt16"))
242     return argIs (pl->line + 5, what);
243 
244   // Todo: addc, subc, xch
245 
246   return true;
247 }
248 
249 static bool
stm8SurelyWritesFlag(const lineNode * pl,const char * what)250 stm8SurelyWritesFlag(const lineNode *pl, const char *what)
251 {
252   if (ISINST (pl->line, "pop") && argIs (pl->line + 4, "af") || ISINST (pl->line, "popaf"))
253     return true;
254 
255   // Instructions that write all flags
256   if (ISINST (pl->line, "add") ||
257     ISINST (pl->line, "addc") ||
258     ISINST (pl->line, "ceqsn") ||
259     ISINST (pl->line, "cneqsn") ||
260     ISINST (pl->line, "dec") ||
261     ISINST (pl->line, "dzsn") ||
262     ISINST (pl->line, "inc") ||
263     ISINST (pl->line, "izsn") ||
264     ISINST (pl->line, "sub") ||
265     ISINST (pl->line, "subc"))
266       return true;
267 
268   // Instructions that write c only
269   if (ISINST (pl->line, "sl") ||
270     ISINST (pl->line, "slc") ||
271     ISINST (pl->line, "sr") ||
272     ISINST (pl->line, "src"))
273       return !strcmp(what, "c");
274 
275   // Instructions that write z only
276   if (ISINST (pl->line, "and") ||
277     ISINST (pl->line, "neg") ||
278     ISINST (pl->line, "not") ||
279     ISINST (pl->line, "or") ||
280     ISINST (pl->line, "xor"))
281       return !strcmp(what, "z");
282 
283   // mov writes z when the destination is a and hte source not an immediate only.
284   if (ISINST (pl->line, "mov") && !strcmp(what, "z") && pl->line[4] == 'a' && pl->line[5] == ',' && !strchr(pl->line, '#'))
285     return true;
286 
287   return false;
288 }
289 
290 static bool
pdkSurelyWrites(const lineNode * pl,const char * what)291 pdkSurelyWrites(const lineNode *pl, const char *what)
292 {
293   if (!strcmp(what, "z") || !strcmp(what, "c") || !strcmp(what, "ac") || !strcmp(what, "ov"))
294     return (stm8SurelyWritesFlag(pl, what));
295 
296   if (ISINST(pl->line, "mov") || ISINST(pl->line, "idxm"))
297     return argIs (pl->line + 4, what);
298 
299   if (ISINST (pl->line, "pop") && argIs (pl->line + 4, "af") || ISINST (pl->line, "popaf"))
300     return !strcmp(what, "a");
301 
302   // TODO: Other instructions
303 
304   // One-operand instructions that write their argument
305   if (ISINST (pl->line, "neg") ||
306     ISINST (pl->line, "not") ||
307     ISINST (pl->line, "sl") ||
308     ISINST (pl->line, "slc") ||
309     ISINST (pl->line, "sr") ||
310     ISINST (pl->line, "src"))
311     return argIs (pl->line + 3, what);
312   if (ISINST (pl->line, "dzsn") ||
313     ISINST (pl->line, "izsn") ||
314     ISINST (pl->line, "ldt16"))
315     return argIs (pl->line + 5, what);
316 
317   return false;
318 }
319 
320 
321 static bool
pdkUncondJump(const lineNode * pl)322 pdkUncondJump(const lineNode *pl)
323 {
324   return (ISINST(pl->line, "goto") || ISINST(pl->line, "pcadd"));
325 }
326 
327 static bool
pdkCondJump(const lineNode * pl)328 pdkCondJump(const lineNode *pl)
329 {
330   return (ISINST(pl->line, "ceqsn") || ISINST(pl->line, "cneqsn") ||
331     ISINST(pl->line, "t0sn") || ISINST(pl->line, "t1sn") ||
332     ISINST(pl->line, "izsn") || ISINST(pl->line, "dzsn"));
333 }
334 
335 static bool
pdkSurelyReturns(const lineNode * pl)336 pdkSurelyReturns(const lineNode *pl)
337 {
338   return(ISINST(pl->line, "ret"));
339 }
340 
341 /*-----------------------------------------------------------------*/
342 /* scan4op - "executes" and examines the assembler opcodes,        */
343 /* follows conditional and un-conditional jumps.                   */
344 /* Moreover it registers all passed labels.                        */
345 /*                                                                 */
346 /* Parameter:                                                      */
347 /*    lineNode **pl                                                */
348 /*       scanning starts from pl;                                  */
349 /*       pl also returns the last scanned line                     */
350 /*    const char *pReg                                             */
351 /*       points to a register (e.g. "ar0"). scan4op() tests for    */
352 /*       read or write operations with this register               */
353 /*    const char *untilOp                                          */
354 /*       points to NULL or a opcode (e.g. "push").                 */
355 /*       scan4op() returns if it hits this opcode.                 */
356 /*    lineNode **plCond                                            */
357 /*       If a conditional branch is met plCond points to the       */
358 /*       lineNode of the conditional branch                        */
359 /*                                                                 */
360 /* Returns:                                                        */
361 /*    S4O_ABORT                                                    */
362 /*       on error                                                  */
363 /*    S4O_VISITED                                                  */
364 /*       hit lineNode with "visited" flag set: scan4op() already   */
365 /*       scanned this opcode.                                      */
366 /*    S4O_FOUNDOPCODE                                              */
367 /*       found opcode and operand, to which untilOp and pReg are   */
368 /*       pointing to.                                              */
369 /*    S4O_RD_OP, S4O_WR_OP                                         */
370 /*       hit an opcode reading or writing from pReg                */
371 /*    S4O_CONDJMP                                                  */
372 /*       hit a conditional jump opcode. pl and plCond return the   */
373 /*       two possible branches.                                    */
374 /*    S4O_TERM                                                     */
375 /*       acall, lcall, ret and reti "terminate" a scan.            */
376 /*-----------------------------------------------------------------*/
377 static S4O_RET
scan4op(lineNode ** pl,const char * what,const char * untilOp,lineNode ** plCond)378 scan4op (lineNode **pl, const char *what, const char *untilOp,
379          lineNode **plCond)
380 {
381   for (; *pl; *pl = (*pl)->next)
382     {
383       if (!(*pl)->line || (*pl)->isDebug || (*pl)->isComment || (*pl)->isLabel)
384         continue;
385       D(("Scanning %s for %s\n", (*pl)->line, what));
386       /* don't optimize across inline assembler,
387          e.g. isLabel doesn't work there */
388       if ((*pl)->isInline)
389         {
390           D(("S4O_ABORT at inline asm\n"));
391           return S4O_ABORT;
392         }
393 
394       if ((*pl)->visited)
395         {
396           D(("S4O_VISITED\n"));
397           return S4O_VISITED;
398         }
399 
400       (*pl)->visited = TRUE;
401 
402       if(pdkMightRead(*pl, what))
403         {
404           D(("S4O_RD_OP\n"));
405           return S4O_RD_OP;
406         }
407 
408       // Check writes before conditional jumps, some jumps (btjf, btjt) write 'c'
409       if(pdkSurelyWrites(*pl, what))
410         {
411           D(("S4O_WR_OP\n"));
412           return S4O_WR_OP;
413         }
414 
415       if(pdkUncondJump(*pl))
416         {
417           *pl = findLabel (*pl);
418             if (!*pl)
419               {
420                 D(("S4O_ABORT at unconditional jump\n"));
421                 return S4O_ABORT;
422               }
423         }
424       if(pdkCondJump(*pl))
425         {
426           *plCond = (*pl)->next->next;
427           if (!*plCond)
428             {
429               D(("S4O_ABORT at conditional jump\n"));
430               return S4O_ABORT;
431             }
432           D(("S4O_CONDJMP\n"));
433           return S4O_CONDJMP;
434         }
435 
436       /* Don't need to check for de, hl since stm8MightRead() does that */
437       if(pdkSurelyReturns(*pl))
438         {
439           D(("S4O_TERM\n"));
440           return S4O_TERM;
441         }
442     }
443   D(("S4O_ABORT\n"));
444   return S4O_ABORT;
445 }
446 
447 /*-----------------------------------------------------------------*/
448 /* doTermScan - scan through area 2. This small wrapper handles:   */
449 /* - action required on different return values                    */
450 /* - recursion in case of conditional branches                     */
451 /*-----------------------------------------------------------------*/
452 static bool
doTermScan(lineNode ** pl,const char * what)453 doTermScan (lineNode **pl, const char *what)
454 {
455   lineNode *plConditional;
456   for (;; *pl = (*pl)->next)
457     {
458       switch (scan4op (pl, what, NULL, &plConditional))
459         {
460           case S4O_TERM:
461           case S4O_VISITED:
462           case S4O_WR_OP:
463             /* all these are terminating conditions */
464             return true;
465           case S4O_CONDJMP:
466             /* two possible destinations: recurse */
467               {
468                 lineNode *pl2 = plConditional;
469                 D(("CONDJMP trying other branch first\n"));
470                 if (!doTermScan (&pl2, what))
471                   return false;
472                 D(("Other branch OK.\n"));
473               }
474             continue;
475           case S4O_RD_OP:
476           default:
477             /* no go */
478             return false;
479         }
480     }
481 }
482 
483 /*-----------------------------------------------------------------*/
484 /* univisitLines - clear "visited" flag in all lines               */
485 /*-----------------------------------------------------------------*/
486 static void
unvisitLines(lineNode * pl)487 unvisitLines (lineNode *pl)
488 {
489   for (; pl; pl = pl->next)
490     pl->visited = false;
491 }
492 
pdknotUsed(const char * what,lineNode * endPl,lineNode * head)493 bool pdknotUsed(const char *what, lineNode *endPl, lineNode *head)
494 {
495   lineNode *pl;
496 
497   _G.head = head;
498 
499   unvisitLines (_G.head);
500 
501   pl = endPl->next;
502   return (doTermScan (&pl, what));
503 }
504 
pdknotUsedFrom(const char * what,const char * label,lineNode * head)505 bool pdknotUsedFrom(const char *what, const char *label, lineNode *head)
506 {
507   lineNode *cpl;
508 
509   for (cpl = head; cpl; cpl = cpl->next)
510     if (cpl->isLabel && !strncmp (label, cpl->line, strlen(label)))
511       return (pdknotUsed (what, cpl, head));
512 
513   return false;
514 }
515 
516 int
pdkinstructionSize(lineNode * pl)517 pdkinstructionSize(lineNode *pl)
518 {
519   return 1;
520 }
521 
522