struct / union in initializer, RFE #901.
[sdcc.git] / sdcc / src / mos6502 / peep.c
blob0e3f0ff0498bf136193252012331058d1ba0f2e2
1 #include "common.h"
2 #include "SDCCgen.h"
4 #include "peep.h"
6 #define NOTUSEDERROR() do {werror(E_INTERNAL_ERROR, __FILE__, __LINE__, "error in notUsed()");} while(0)
8 // #define D(_s) { printf _s; fflush(stdout); }
9 #define D(_s)
11 #define ISINST(l, i) (!STRNCASECMP((l), (i), sizeof(i) - 1) && (!(l)[sizeof(i) - 1] || isspace((unsigned char)((l)[sizeof(i) - 1]))))
13 typedef enum
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;
24 static struct
26 lineNode *head;
27 } _G;
29 /*-----------------------------------------------------------------*/
30 /* incLabelJmpToCount - increment counter "jmpToCount" in entry */
31 /* of the list labelHash */
32 /*-----------------------------------------------------------------*/
33 static bool
34 incLabelJmpToCount (const char *label)
36 labelHashEntry *entry;
38 entry = getLabelRef (label, _G.head);
39 if (!entry)
40 return FALSE;
41 entry->jmpToCount++;
42 return TRUE;
45 /*-----------------------------------------------------------------*/
46 /* findLabel - */
47 /* 1. extracts label in the opcode pl */
48 /* 2. increment "label jump-to count" in labelHash */
49 /* 3. search lineNode with label definition and return it */
50 /*-----------------------------------------------------------------*/
51 static lineNode *
52 findLabel (const lineNode *pl)
54 char *p;
55 lineNode *cpl;
57 /* 1. extract label in opcode */
59 /* In each jump the label is at the end */
60 p = strlen (pl->line) - 1 + pl->line;
62 /* Skip trailing whitespace */
63 while(isspace(*p))
64 p--;
66 /* scan backward until space or ',' */
67 for (; p > pl->line; p--)
68 if (isspace(*p) || *p == ',')
69 break;
71 /* sanity check */
72 if (p == pl->line)
74 NOTUSEDERROR();
75 return NULL;
78 /* skip ',' resp. '\t' */
79 ++p;
81 /* 2. increment "label jump-to count" */
82 if (!incLabelJmpToCount (p))
83 return NULL;
85 /* 3. search lineNode with label definition and return it */
86 for (cpl = _G.head; cpl; cpl = cpl->next)
87 if (cpl->isLabel &&
88 strncmp (p, cpl->line, strlen(p)) == 0 &&
89 cpl->line[strlen(p)] == ':')
90 return cpl;
92 return NULL;
95 static bool
96 mos6502MightReadFlag(const lineNode *pl, const char *what)
98 if (ISINST (pl->line, "adc") ||
99 ISINST (pl->line, "rol") ||
100 ISINST (pl->line, "ror") ||
101 ISINST (pl->line, "sbc"))
102 return (!strcmp(what, "c"));
104 if (ISINST (pl->line, "bcc") || ISINST (pl->line, "bcs"))
105 return (!strcmp(what, "c"));
107 if (ISINST (pl->line, "beq") || ISINST (pl->line, "bne"))
108 return (!strcmp(what, "z"));
110 if (ISINST (pl->line, "bmi") || ISINST (pl->line, "bpl"))
111 return (!strcmp(what, "n"));
113 if (ISINST (pl->line, "bvc") || ISINST (pl->line, "bvs"))
114 return (!strcmp(what, "v"));
116 return false;
119 static bool
120 mos6502MightRead(const lineNode *pl, const char *what)
122 if (!strcmp (what, "n") || !strcmp (what, "z") || !strcmp (what, "c") || !strcmp (what, "v"))
123 return (mos6502MightReadFlag (pl, what));
125 return true;
128 static bool
129 mos6502SurelyWritesFlag(const lineNode *pl, const char *what)
131 if (ISINST (pl->line, "adc") ||
132 ISINST (pl->line, "sbc"))
133 return (!strcmp(what, "n") || !strcmp(what, "z") || !strcmp(what, "c") || !strcmp(what, "v"));
135 if (ISINST (pl->line, "asl") ||
136 ISINST (pl->line, "cmp") ||
137 ISINST (pl->line, "cpx") ||
138 ISINST (pl->line, "cpy") ||
139 ISINST (pl->line, "lsr") ||
140 ISINST (pl->line, "rol") ||
141 ISINST (pl->line, "ror"))
142 return (!strcmp(what, "n") || !strcmp(what, "z") || !strcmp(what, "c"));
144 if (ISINST (pl->line, "and") ||
145 ISINST (pl->line, "dec") ||
146 ISINST (pl->line, "dex") ||
147 ISINST (pl->line, "dey") ||
148 ISINST (pl->line, "eor") ||
149 ISINST (pl->line, "inc") ||
150 ISINST (pl->line, "inx") ||
151 ISINST (pl->line, "iny") ||
152 ISINST (pl->line, "lda") ||
153 ISINST (pl->line, "ldx") ||
154 ISINST (pl->line, "ldy") ||
155 ISINST (pl->line, "ora") ||
156 ISINST (pl->line, "pla") ||
157 ISINST (pl->line, "tax") ||
158 ISINST (pl->line, "tay") ||
159 ISINST (pl->line, "tsx") ||
160 ISINST (pl->line, "tsa") ||
161 ISINST (pl->line, "tya"))
162 return (!strcmp(what, "n") || !strcmp(what, "z"));
164 if (ISINST (pl->line, "bit"))
165 return (!strcmp(what, "n") || !strcmp(what, "z") || !strcmp(what, "v"));
167 if (ISINST (pl->line, "clc") ||
168 ISINST (pl->line, "sec"))
169 return (!strcmp(what, "c"));
171 if (ISINST (pl->line, "clv"))
172 return (!strcmp(what, "v"));
174 if (ISINST (pl->line, "plp") ||
175 ISINST (pl->line, "rti"))
176 return true;
178 return false;
181 static bool
182 mos6502SurelyWrites(const lineNode *pl, const char *what)
184 if (!strcmp (what, "n") || !strcmp (what, "z") || !strcmp (what, "c") || !strcmp (what, "v"))
185 return (mos6502SurelyWritesFlag(pl, what));
187 return false;
191 static bool
192 mos6502UncondJump (const lineNode *pl)
194 return (ISINST (pl->line, "jmp"));
197 static bool
198 mos6502CondJump (const lineNode *pl)
200 return (ISINST (pl->line, "bpl") || ISINST (pl->line, "bmi") ||
201 ISINST (pl->line, "bvc") || ISINST (pl->line, "bvs") ||
202 ISINST (pl->line, "bcc") || ISINST (pl->line, "bcs") ||
203 ISINST (pl->line, "bne") || ISINST (pl->line, "beq"));
206 static bool
207 mos6502SurelyReturns (const lineNode *pl)
209 return (ISINST (pl->line, "rts") || ISINST (pl->line, "rti") );
212 /*-----------------------------------------------------------------*/
213 /* scan4op - "executes" and examines the assembler opcodes, */
214 /* follows conditional and un-conditional jumps. */
215 /* Moreover it registers all passed labels. */
216 /* */
217 /* Parameter: */
218 /* lineNode **pl */
219 /* scanning starts from pl; */
220 /* pl also returns the last scanned line */
221 /* const char *pReg */
222 /* points to a register (e.g. "ar0"). scan4op() tests for */
223 /* read or write operations with this register */
224 /* const char *untilOp */
225 /* points to NULL or a opcode (e.g. "push"). */
226 /* scan4op() returns if it hits this opcode. */
227 /* lineNode **plCond */
228 /* If a conditional branch is met plCond points to the */
229 /* lineNode of the conditional branch */
230 /* */
231 /* Returns: */
232 /* S4O_ABORT */
233 /* on error */
234 /* S4O_VISITED */
235 /* hit lineNode with "visited" flag set: scan4op() already */
236 /* scanned this opcode. */
237 /* S4O_FOUNDOPCODE */
238 /* found opcode and operand, to which untilOp and pReg are */
239 /* pointing to. */
240 /* S4O_RD_OP, S4O_WR_OP */
241 /* hit an opcode reading or writing from pReg */
242 /* S4O_CONDJMP */
243 /* hit a conditional jump opcode. pl and plCond return the */
244 /* two possible branches. */
245 /* S4O_TERM */
246 /* acall, lcall, ret and reti "terminate" a scan. */
247 /*-----------------------------------------------------------------*/
248 static S4O_RET
249 scan4op (lineNode **pl, const char *what, const char *untilOp,
250 lineNode **plCond)
252 for (; *pl; *pl = (*pl)->next)
254 if (!(*pl)->line || (*pl)->isDebug || (*pl)->isComment || (*pl)->isLabel)
255 continue;
256 D(("Scanning %s for %s\n", (*pl)->line, what));
257 /* don't optimize across inline assembler,
258 e.g. isLabel doesn't work there */
259 if ((*pl)->isInline)
261 D(("S4O_ABORT at inline asm\n"));
262 return S4O_ABORT;
265 if ((*pl)->visited)
267 D(("S4O_VISITED\n"));
268 return S4O_VISITED;
271 (*pl)->visited = TRUE;
273 if (mos6502MightRead (*pl, what))
275 D(("S4O_RD_OP\n"));
276 return S4O_RD_OP;
279 // Check writes before conditional jumps, some jumps (btjf, btjt) write 'c'
280 if (mos6502SurelyWrites (*pl, what))
282 D(("S4O_WR_OP\n"));
283 return S4O_WR_OP;
286 if (mos6502UncondJump (*pl))
288 *pl = findLabel (*pl);
289 if (!*pl)
291 D(("S4O_ABORT at unconditional jump\n"));
292 return S4O_ABORT;
295 if (mos6502CondJump (*pl))
297 *plCond = (*pl)->next->next;
298 if (!*plCond)
300 D(("S4O_ABORT at conditional jump\n"));
301 return S4O_ABORT;
303 D(("S4O_CONDJMP\n"));
304 return S4O_CONDJMP;
307 /* Don't need to check for de, hl since pdkMightRead() does that */
308 if (mos6502SurelyReturns (*pl))
310 D(("S4O_TERM\n"));
311 return S4O_TERM;
314 D(("S4O_ABORT\n"));
315 return S4O_ABORT;
318 /*-----------------------------------------------------------------*/
319 /* doTermScan - scan through area 2. This small wrapper handles: */
320 /* - action required on different return values */
321 /* - recursion in case of conditional branches */
322 /*-----------------------------------------------------------------*/
323 static bool
324 doTermScan (lineNode **pl, const char *what)
326 lineNode *plConditional;
327 for (;; *pl = (*pl)->next)
329 switch (scan4op (pl, what, NULL, &plConditional))
331 case S4O_TERM:
332 case S4O_VISITED:
333 case S4O_WR_OP:
334 /* all these are terminating conditions */
335 return true;
336 case S4O_CONDJMP:
337 /* two possible destinations: recurse */
339 lineNode *pl2 = plConditional;
340 D(("CONDJMP trying other branch first\n"));
341 if (!doTermScan (&pl2, what))
342 return false;
343 D(("Other branch OK.\n"));
345 continue;
346 case S4O_RD_OP:
347 default:
348 /* no go */
349 return false;
354 /*-----------------------------------------------------------------*/
355 /* univisitLines - clear "visited" flag in all lines */
356 /*-----------------------------------------------------------------*/
357 static void
358 unvisitLines (lineNode *pl)
360 for (; pl; pl = pl->next)
361 pl->visited = false;
364 bool mos6502notUsed (const char *what, lineNode *endPl, lineNode *head)
366 lineNode *pl;
368 _G.head = head;
370 unvisitLines (_G.head);
372 // Todo: Implement WDC 65C02 support, remove this check!
373 if (TARGET_IS_MOS65C02)
374 return (false);
376 pl = endPl->next;
377 return (doTermScan (&pl, what));
380 bool mos6502notUsedFrom (const char *what, const char *label, lineNode *head)
382 lineNode *cpl;
384 for (cpl = head; cpl; cpl = cpl->next)
385 if (cpl->isLabel && !strncmp (label, cpl->line, strlen(label)))
386 return (mos6502notUsed (what, cpl, head));
388 return false;