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