/src/binutils-gdb/gas/ginsn.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* ginsn.h - GAS instruction representation. |
2 | | Copyright (C) 2023 Free Software Foundation, Inc. |
3 | | |
4 | | This file is part of GAS, the GNU Assembler. |
5 | | |
6 | | GAS is free software; you can redistribute it and/or modify |
7 | | it under the terms of the GNU General Public License as published by |
8 | | the Free Software Foundation; either version 3, or (at your option) |
9 | | any later version. |
10 | | |
11 | | GAS is distributed in the hope that it will be useful, |
12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | GNU General Public License for more details. |
15 | | |
16 | | You should have received a copy of the GNU General Public License |
17 | | along with GAS; see the file COPYING. If not, write to the Free |
18 | | Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA |
19 | | 02110-1301, USA. */ |
20 | | |
21 | | #include "as.h" |
22 | | #include "subsegs.h" |
23 | | #include "ginsn.h" |
24 | | #include "scfi.h" |
25 | | |
26 | | #ifdef TARGET_USE_GINSN |
27 | | |
28 | | static const char *const ginsn_type_names[] = |
29 | | { |
30 | | #define _GINSN_TYPE_ITEM(NAME, STR) STR, |
31 | | _GINSN_TYPES |
32 | | #undef _GINSN_TYPE_ITEM |
33 | | }; |
34 | | |
35 | | static ginsnS * |
36 | | ginsn_alloc (void) |
37 | 0 | { |
38 | 0 | ginsnS *ginsn = XCNEW (ginsnS); |
39 | 0 | return ginsn; |
40 | 0 | } |
41 | | |
42 | | static ginsnS * |
43 | | ginsn_init (enum ginsn_type type, const symbolS *sym, bool real_p) |
44 | 0 | { |
45 | 0 | ginsnS *ginsn = ginsn_alloc (); |
46 | 0 | ginsn->type = type; |
47 | 0 | ginsn->sym = sym; |
48 | 0 | if (real_p) |
49 | 0 | ginsn->flags |= GINSN_F_INSN_REAL; |
50 | 0 | return ginsn; |
51 | 0 | } |
52 | | |
53 | | static void |
54 | | ginsn_cleanup (ginsnS **ginsnp) |
55 | 0 | { |
56 | 0 | ginsnS *ginsn; |
57 | |
|
58 | 0 | if (!ginsnp || !*ginsnp) |
59 | 0 | return; |
60 | | |
61 | 0 | ginsn = *ginsnp; |
62 | 0 | if (ginsn->scfi_ops) |
63 | 0 | { |
64 | 0 | scfi_ops_cleanup (ginsn->scfi_ops); |
65 | 0 | ginsn->scfi_ops = NULL; |
66 | 0 | } |
67 | |
|
68 | 0 | free (ginsn); |
69 | 0 | *ginsnp = NULL; |
70 | 0 | } |
71 | | |
72 | | static void |
73 | | ginsn_set_src (struct ginsn_src *src, enum ginsn_src_type type, unsigned int reg, |
74 | | offsetT immdisp) |
75 | 0 | { |
76 | 0 | if (!src) |
77 | 0 | return; |
78 | | |
79 | 0 | src->type = type; |
80 | | /* Even when the use-case is SCFI, the value of reg may be > SCFI_MAX_REG_ID. |
81 | | E.g., in AMD64, push fs etc. */ |
82 | 0 | src->reg = reg; |
83 | 0 | src->immdisp = immdisp; |
84 | 0 | } |
85 | | |
86 | | static void |
87 | | ginsn_set_dst (struct ginsn_dst *dst, enum ginsn_dst_type type, unsigned int reg, |
88 | | offsetT disp) |
89 | 0 | { |
90 | 0 | if (!dst) |
91 | 0 | return; |
92 | | |
93 | 0 | dst->type = type; |
94 | 0 | dst->reg = reg; |
95 | |
|
96 | 0 | if (type == GINSN_DST_INDIRECT) |
97 | 0 | dst->disp = disp; |
98 | 0 | } |
99 | | |
100 | | static void |
101 | | ginsn_set_file_line (ginsnS *ginsn, const char *file, unsigned int line) |
102 | 0 | { |
103 | 0 | if (!ginsn) |
104 | 0 | return; |
105 | | |
106 | 0 | ginsn->file = file; |
107 | 0 | ginsn->line = line; |
108 | 0 | } |
109 | | |
110 | | struct ginsn_src * |
111 | | ginsn_get_src1 (ginsnS *ginsn) |
112 | 0 | { |
113 | 0 | return &ginsn->src[0]; |
114 | 0 | } |
115 | | |
116 | | struct ginsn_src * |
117 | | ginsn_get_src2 (ginsnS *ginsn) |
118 | 0 | { |
119 | 0 | return &ginsn->src[1]; |
120 | 0 | } |
121 | | |
122 | | struct ginsn_dst * |
123 | | ginsn_get_dst (ginsnS *ginsn) |
124 | 0 | { |
125 | 0 | return &ginsn->dst; |
126 | 0 | } |
127 | | |
128 | | unsigned int |
129 | | ginsn_get_src_reg (struct ginsn_src *src) |
130 | 0 | { |
131 | 0 | return src->reg; |
132 | 0 | } |
133 | | |
134 | | enum ginsn_src_type |
135 | | ginsn_get_src_type (struct ginsn_src *src) |
136 | 0 | { |
137 | 0 | return src->type; |
138 | 0 | } |
139 | | |
140 | | offsetT |
141 | | ginsn_get_src_disp (struct ginsn_src *src) |
142 | 0 | { |
143 | 0 | return src->immdisp; |
144 | 0 | } |
145 | | |
146 | | offsetT |
147 | | ginsn_get_src_imm (struct ginsn_src *src) |
148 | 0 | { |
149 | 0 | return src->immdisp; |
150 | 0 | } |
151 | | |
152 | | unsigned int |
153 | | ginsn_get_dst_reg (struct ginsn_dst *dst) |
154 | 0 | { |
155 | 0 | return dst->reg; |
156 | 0 | } |
157 | | |
158 | | enum ginsn_dst_type |
159 | | ginsn_get_dst_type (struct ginsn_dst *dst) |
160 | 0 | { |
161 | 0 | return dst->type; |
162 | 0 | } |
163 | | |
164 | | offsetT |
165 | | ginsn_get_dst_disp (struct ginsn_dst *dst) |
166 | 0 | { |
167 | 0 | return dst->disp; |
168 | 0 | } |
169 | | |
170 | | void |
171 | | label_ginsn_map_insert (const symbolS *label, ginsnS *ginsn) |
172 | 0 | { |
173 | 0 | const char *name = S_GET_NAME (label); |
174 | 0 | str_hash_insert (frchain_now->frch_ginsn_data->label_ginsn_map, |
175 | 0 | name, ginsn, 0 /* noreplace. */); |
176 | 0 | } |
177 | | |
178 | | ginsnS * |
179 | | label_ginsn_map_find (const symbolS *label) |
180 | 0 | { |
181 | 0 | const char *name = S_GET_NAME (label); |
182 | 0 | ginsnS *ginsn |
183 | 0 | = (ginsnS *) str_hash_find (frchain_now->frch_ginsn_data->label_ginsn_map, |
184 | 0 | name); |
185 | 0 | return ginsn; |
186 | 0 | } |
187 | | |
188 | | ginsnS * |
189 | | ginsn_new_phantom (const symbolS *sym) |
190 | 0 | { |
191 | 0 | ginsnS *ginsn = ginsn_alloc (); |
192 | 0 | ginsn->type = GINSN_TYPE_PHANTOM; |
193 | 0 | ginsn->sym = sym; |
194 | | /* By default, GINSN_F_INSN_REAL is not set in ginsn->flags. */ |
195 | 0 | return ginsn; |
196 | 0 | } |
197 | | |
198 | | ginsnS * |
199 | | ginsn_new_symbol (const symbolS *sym, bool func_begin_p) |
200 | 0 | { |
201 | 0 | ginsnS *ginsn = ginsn_alloc (); |
202 | 0 | ginsn->type = GINSN_TYPE_SYMBOL; |
203 | 0 | ginsn->sym = sym; |
204 | 0 | if (func_begin_p) |
205 | 0 | ginsn->flags |= GINSN_F_FUNC_MARKER; |
206 | 0 | return ginsn; |
207 | 0 | } |
208 | | |
209 | | ginsnS * |
210 | | ginsn_new_symbol_func_begin (const symbolS *sym) |
211 | 0 | { |
212 | 0 | return ginsn_new_symbol (sym, true); |
213 | 0 | } |
214 | | |
215 | | ginsnS * |
216 | | ginsn_new_symbol_func_end (const symbolS *sym) |
217 | 0 | { |
218 | 0 | return ginsn_new_symbol (sym, false); |
219 | 0 | } |
220 | | |
221 | | ginsnS * |
222 | | ginsn_new_symbol_user_label (const symbolS *sym) |
223 | 0 | { |
224 | 0 | ginsnS *ginsn = ginsn_new_symbol (sym, false); |
225 | 0 | ginsn->flags |= GINSN_F_USER_LABEL; |
226 | 0 | return ginsn; |
227 | 0 | } |
228 | | |
229 | | ginsnS * |
230 | | ginsn_new_add (const symbolS *sym, bool real_p, |
231 | | enum ginsn_src_type src1_type, unsigned int src1_reg, offsetT src1_disp, |
232 | | enum ginsn_src_type src2_type, unsigned int src2_reg, offsetT src2_disp, |
233 | | enum ginsn_dst_type dst_type, unsigned int dst_reg, offsetT dst_disp) |
234 | 0 | { |
235 | 0 | ginsnS *ginsn = ginsn_init (GINSN_TYPE_ADD, sym, real_p); |
236 | | /* src info. */ |
237 | 0 | ginsn_set_src (&ginsn->src[0], src1_type, src1_reg, src1_disp); |
238 | 0 | ginsn_set_src (&ginsn->src[1], src2_type, src2_reg, src2_disp); |
239 | | /* dst info. */ |
240 | 0 | ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp); |
241 | |
|
242 | 0 | return ginsn; |
243 | 0 | } |
244 | | |
245 | | ginsnS * |
246 | | ginsn_new_and (const symbolS *sym, bool real_p, |
247 | | enum ginsn_src_type src1_type, unsigned int src1_reg, offsetT src1_disp, |
248 | | enum ginsn_src_type src2_type, unsigned int src2_reg, offsetT src2_disp, |
249 | | enum ginsn_dst_type dst_type, unsigned int dst_reg, offsetT dst_disp) |
250 | 0 | { |
251 | 0 | ginsnS *ginsn = ginsn_init (GINSN_TYPE_AND, sym, real_p); |
252 | | /* src info. */ |
253 | 0 | ginsn_set_src (&ginsn->src[0], src1_type, src1_reg, src1_disp); |
254 | 0 | ginsn_set_src (&ginsn->src[1], src2_type, src2_reg, src2_disp); |
255 | | /* dst info. */ |
256 | 0 | ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp); |
257 | |
|
258 | 0 | return ginsn; |
259 | 0 | } |
260 | | |
261 | | ginsnS * |
262 | | ginsn_new_call (const symbolS *sym, bool real_p, |
263 | | enum ginsn_src_type src_type, unsigned int src_reg, |
264 | | const symbolS *src_text_sym) |
265 | | |
266 | 0 | { |
267 | 0 | ginsnS *ginsn = ginsn_init (GINSN_TYPE_CALL, sym, real_p); |
268 | | /* src info. */ |
269 | 0 | ginsn_set_src (&ginsn->src[0], src_type, src_reg, 0); |
270 | |
|
271 | 0 | if (src_type == GINSN_SRC_SYMBOL) |
272 | 0 | ginsn->src[0].sym = src_text_sym; |
273 | |
|
274 | 0 | return ginsn; |
275 | 0 | } |
276 | | |
277 | | ginsnS * |
278 | | ginsn_new_jump (const symbolS *sym, bool real_p, |
279 | | enum ginsn_src_type src_type, unsigned int src_reg, |
280 | | const symbolS *src_ginsn_sym) |
281 | 0 | { |
282 | 0 | ginsnS *ginsn = ginsn_init (GINSN_TYPE_JUMP, sym, real_p); |
283 | | /* src info. */ |
284 | 0 | ginsn_set_src (&ginsn->src[0], src_type, src_reg, 0); |
285 | |
|
286 | 0 | if (src_type == GINSN_SRC_SYMBOL) |
287 | 0 | ginsn->src[0].sym = src_ginsn_sym; |
288 | |
|
289 | 0 | return ginsn; |
290 | 0 | } |
291 | | |
292 | | ginsnS * |
293 | | ginsn_new_jump_cond (const symbolS *sym, bool real_p, |
294 | | enum ginsn_src_type src_type, unsigned int src_reg, |
295 | | const symbolS *src_ginsn_sym) |
296 | 0 | { |
297 | 0 | ginsnS *ginsn = ginsn_init (GINSN_TYPE_JUMP_COND, sym, real_p); |
298 | | /* src info. */ |
299 | 0 | ginsn_set_src (&ginsn->src[0], src_type, src_reg, 0); |
300 | |
|
301 | 0 | if (src_type == GINSN_SRC_SYMBOL) |
302 | 0 | ginsn->src[0].sym = src_ginsn_sym; |
303 | |
|
304 | 0 | return ginsn; |
305 | 0 | } |
306 | | |
307 | | ginsnS * |
308 | | ginsn_new_mov (const symbolS *sym, bool real_p, |
309 | | enum ginsn_src_type src_type, unsigned int src_reg, offsetT src_disp, |
310 | | enum ginsn_dst_type dst_type, unsigned int dst_reg, offsetT dst_disp) |
311 | 0 | { |
312 | 0 | ginsnS *ginsn = ginsn_init (GINSN_TYPE_MOV, sym, real_p); |
313 | | /* src info. */ |
314 | 0 | ginsn_set_src (&ginsn->src[0], src_type, src_reg, src_disp); |
315 | | /* dst info. */ |
316 | 0 | ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp); |
317 | |
|
318 | 0 | return ginsn; |
319 | 0 | } |
320 | | |
321 | | ginsnS * |
322 | | ginsn_new_store (const symbolS *sym, bool real_p, |
323 | | enum ginsn_src_type src_type, unsigned int src_reg, |
324 | | enum ginsn_dst_type dst_type, unsigned int dst_reg, offsetT dst_disp) |
325 | 0 | { |
326 | 0 | ginsnS *ginsn = ginsn_init (GINSN_TYPE_STORE, sym, real_p); |
327 | | /* src info. */ |
328 | 0 | ginsn_set_src (&ginsn->src[0], src_type, src_reg, 0); |
329 | | /* dst info. */ |
330 | 0 | gas_assert (dst_type == GINSN_DST_INDIRECT); |
331 | 0 | ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp); |
332 | |
|
333 | 0 | return ginsn; |
334 | 0 | } |
335 | | |
336 | | ginsnS * |
337 | | ginsn_new_load (const symbolS *sym, bool real_p, |
338 | | enum ginsn_src_type src_type, unsigned int src_reg, offsetT src_disp, |
339 | | enum ginsn_dst_type dst_type, unsigned int dst_reg) |
340 | 0 | { |
341 | 0 | ginsnS *ginsn = ginsn_init (GINSN_TYPE_LOAD, sym, real_p); |
342 | | /* src info. */ |
343 | 0 | gas_assert (src_type == GINSN_SRC_INDIRECT); |
344 | 0 | ginsn_set_src (&ginsn->src[0], src_type, src_reg, src_disp); |
345 | | /* dst info. */ |
346 | 0 | ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, 0); |
347 | |
|
348 | 0 | return ginsn; |
349 | 0 | } |
350 | | |
351 | | ginsnS * |
352 | | ginsn_new_sub (const symbolS *sym, bool real_p, |
353 | | enum ginsn_src_type src1_type, unsigned int src1_reg, offsetT src1_disp, |
354 | | enum ginsn_src_type src2_type, unsigned int src2_reg, offsetT src2_disp, |
355 | | enum ginsn_dst_type dst_type, unsigned int dst_reg, offsetT dst_disp) |
356 | 0 | { |
357 | 0 | ginsnS *ginsn = ginsn_init (GINSN_TYPE_SUB, sym, real_p); |
358 | | /* src info. */ |
359 | 0 | ginsn_set_src (&ginsn->src[0], src1_type, src1_reg, src1_disp); |
360 | 0 | ginsn_set_src (&ginsn->src[1], src2_type, src2_reg, src2_disp); |
361 | | /* dst info. */ |
362 | 0 | ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp); |
363 | |
|
364 | 0 | return ginsn; |
365 | 0 | } |
366 | | |
367 | | /* PS: Note this API does not identify the displacement values of |
368 | | src1/src2/dst. At this time, it is unnecessary for correctness to support |
369 | | the additional argument. */ |
370 | | |
371 | | ginsnS * |
372 | | ginsn_new_other (const symbolS *sym, bool real_p, |
373 | | enum ginsn_src_type src1_type, unsigned int src1_val, |
374 | | enum ginsn_src_type src2_type, unsigned int src2_val, |
375 | | enum ginsn_dst_type dst_type, unsigned int dst_reg) |
376 | 0 | { |
377 | 0 | ginsnS *ginsn = ginsn_init (GINSN_TYPE_OTHER, sym, real_p); |
378 | | /* src info. */ |
379 | 0 | ginsn_set_src (&ginsn->src[0], src1_type, src1_val, src1_val); |
380 | | /* GINSN_SRC_INDIRECT src2_type is not expected. */ |
381 | 0 | gas_assert (src2_type != GINSN_SRC_INDIRECT); |
382 | 0 | ginsn_set_src (&ginsn->src[1], src2_type, src2_val, src2_val); |
383 | | /* dst info. */ |
384 | 0 | ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, 0); |
385 | |
|
386 | 0 | return ginsn; |
387 | 0 | } |
388 | | |
389 | | ginsnS * |
390 | | ginsn_new_return (const symbolS *sym, bool real_p) |
391 | 0 | { |
392 | 0 | ginsnS *ginsn = ginsn_init (GINSN_TYPE_RETURN, sym, real_p); |
393 | 0 | return ginsn; |
394 | 0 | } |
395 | | |
396 | | void |
397 | | ginsn_set_where (ginsnS *ginsn) |
398 | 0 | { |
399 | 0 | const char *file; |
400 | 0 | unsigned int line; |
401 | 0 | file = as_where (&line); |
402 | 0 | ginsn_set_file_line (ginsn, file, line); |
403 | 0 | } |
404 | | |
405 | | int |
406 | | ginsn_link_next (ginsnS *ginsn, ginsnS *next) |
407 | 0 | { |
408 | 0 | int ret = 0; |
409 | | |
410 | | /* Avoid data corruption by limiting the scope of the API. */ |
411 | 0 | if (!ginsn || ginsn->next) |
412 | 0 | return 1; |
413 | | |
414 | 0 | ginsn->next = next; |
415 | |
|
416 | 0 | return ret; |
417 | 0 | } |
418 | | |
419 | | bool |
420 | | ginsn_track_reg_p (unsigned int dw2reg, enum ginsn_gen_mode gmode) |
421 | 0 | { |
422 | 0 | bool track_p = false; |
423 | |
|
424 | 0 | if (gmode == GINSN_GEN_SCFI && dw2reg <= SCFI_MAX_REG_ID) |
425 | 0 | { |
426 | | /* FIXME - rename this to tc_ ? */ |
427 | 0 | track_p |= SCFI_CALLEE_SAVED_REG_P (dw2reg); |
428 | 0 | track_p |= (dw2reg == REG_FP); |
429 | 0 | track_p |= (dw2reg == REG_SP); |
430 | 0 | } |
431 | |
|
432 | 0 | return track_p; |
433 | 0 | } |
434 | | |
435 | | static bool |
436 | | ginsn_indirect_jump_p (ginsnS *ginsn) |
437 | 0 | { |
438 | 0 | bool ret_p = false; |
439 | 0 | if (!ginsn) |
440 | 0 | return ret_p; |
441 | | |
442 | 0 | ret_p = (ginsn->type == GINSN_TYPE_JUMP |
443 | 0 | && ginsn->src[0].type == GINSN_SRC_REG); |
444 | 0 | return ret_p; |
445 | 0 | } |
446 | | |
447 | | /* Return whether the GINSN is an unconditional jump to a label which is |
448 | | defined locally in the scope of the block of insns, which are currently |
449 | | being processed for GCFG creation. */ |
450 | | |
451 | | static bool |
452 | | ginsn_direct_local_jump_p (ginsnS *ginsn) |
453 | 0 | { |
454 | 0 | bool local_p = false; |
455 | 0 | const symbolS *taken_label; |
456 | |
|
457 | 0 | if (!ginsn) |
458 | 0 | return local_p; |
459 | | |
460 | 0 | if (ginsn->type == GINSN_TYPE_JUMP |
461 | 0 | && ginsn->src[0].type == GINSN_SRC_SYMBOL) |
462 | 0 | { |
463 | 0 | taken_label = ginsn->src[0].sym; |
464 | 0 | local_p = (label_ginsn_map_find (taken_label) != NULL); |
465 | 0 | } |
466 | 0 | return local_p; |
467 | 0 | } |
468 | | |
469 | | static char * |
470 | | ginsn_src_print (struct ginsn_src *src) |
471 | 0 | { |
472 | 0 | size_t len = 40; |
473 | 0 | char *src_str = XNEWVEC (char, len); |
474 | |
|
475 | 0 | memset (src_str, 0, len); |
476 | |
|
477 | 0 | switch (src->type) |
478 | 0 | { |
479 | 0 | case GINSN_SRC_REG: |
480 | 0 | snprintf (src_str, len, "%%r%d, ", ginsn_get_src_reg (src)); |
481 | 0 | break; |
482 | 0 | case GINSN_SRC_IMM: |
483 | 0 | snprintf (src_str, len, "%lld, ", |
484 | 0 | (long long int) ginsn_get_src_imm (src)); |
485 | 0 | break; |
486 | 0 | case GINSN_SRC_INDIRECT: |
487 | 0 | snprintf (src_str, len, "[%%r%d+%lld], ", ginsn_get_src_reg (src), |
488 | 0 | (long long int) ginsn_get_src_disp (src)); |
489 | 0 | break; |
490 | 0 | default: |
491 | 0 | break; |
492 | 0 | } |
493 | | |
494 | 0 | return src_str; |
495 | 0 | } |
496 | | |
497 | | static char* |
498 | | ginsn_dst_print (struct ginsn_dst *dst) |
499 | 0 | { |
500 | 0 | size_t len = GINSN_LISTING_OPND_LEN; |
501 | 0 | char *dst_str = XNEWVEC (char, len); |
502 | |
|
503 | 0 | memset (dst_str, 0, len); |
504 | |
|
505 | 0 | if (dst->type == GINSN_DST_REG) |
506 | 0 | { |
507 | 0 | char *buf = XNEWVEC (char, 32); |
508 | 0 | sprintf (buf, "%%r%d", ginsn_get_dst_reg (dst)); |
509 | 0 | strcat (dst_str, buf); |
510 | 0 | free (buf); |
511 | 0 | } |
512 | 0 | else if (dst->type == GINSN_DST_INDIRECT) |
513 | 0 | { |
514 | 0 | char *buf = XNEWVEC (char, 32); |
515 | 0 | sprintf (buf, "[%%r%d+%lld]", ginsn_get_dst_reg (dst), |
516 | 0 | (long long int) ginsn_get_dst_disp (dst)); |
517 | 0 | strcat (dst_str, buf); |
518 | 0 | free (buf); |
519 | 0 | } |
520 | |
|
521 | 0 | gas_assert (strlen (dst_str) < GINSN_LISTING_OPND_LEN); |
522 | | |
523 | 0 | return dst_str; |
524 | 0 | } |
525 | | |
526 | | static const char* |
527 | | ginsn_type_func_marker_print (ginsnS *ginsn) |
528 | 0 | { |
529 | 0 | int id = 0; |
530 | 0 | static const char * const ginsn_sym_strs[] = |
531 | 0 | { "", "FUNC_BEGIN", "FUNC_END" }; |
532 | |
|
533 | 0 | if (GINSN_F_FUNC_BEGIN_P (ginsn)) |
534 | 0 | id = 1; |
535 | 0 | else if (GINSN_F_FUNC_END_P (ginsn)) |
536 | 0 | id = 2; |
537 | |
|
538 | 0 | return ginsn_sym_strs[id]; |
539 | 0 | } |
540 | | |
541 | | static char* |
542 | | ginsn_print (ginsnS *ginsn) |
543 | 0 | { |
544 | 0 | struct ginsn_src *src; |
545 | 0 | struct ginsn_dst *dst; |
546 | 0 | int str_size = 0; |
547 | 0 | size_t len = GINSN_LISTING_LEN; |
548 | 0 | char *ginsn_str = XNEWVEC (char, len); |
549 | |
|
550 | 0 | memset (ginsn_str, 0, len); |
551 | |
|
552 | 0 | str_size = snprintf (ginsn_str, GINSN_LISTING_LEN, "ginsn: %s", |
553 | 0 | ginsn_type_names[ginsn->type]); |
554 | 0 | gas_assert (str_size >= 0 && str_size < GINSN_LISTING_LEN); |
555 | | |
556 | | /* For some ginsn types, no further information is printed for now. */ |
557 | 0 | if (ginsn->type == GINSN_TYPE_CALL |
558 | 0 | || ginsn->type == GINSN_TYPE_RETURN) |
559 | 0 | goto end; |
560 | 0 | else if (ginsn->type == GINSN_TYPE_SYMBOL) |
561 | 0 | { |
562 | 0 | if (GINSN_F_USER_LABEL_P (ginsn)) |
563 | 0 | str_size += snprintf (ginsn_str + str_size, |
564 | 0 | GINSN_LISTING_LEN - str_size, |
565 | 0 | " %s", S_GET_NAME (ginsn->sym)); |
566 | 0 | else |
567 | 0 | str_size += snprintf (ginsn_str + str_size, |
568 | 0 | GINSN_LISTING_LEN - str_size, |
569 | 0 | " %s", ginsn_type_func_marker_print (ginsn)); |
570 | 0 | goto end; |
571 | 0 | } |
572 | | |
573 | | /* src 1. */ |
574 | 0 | src = ginsn_get_src1 (ginsn); |
575 | 0 | char *src_buf = ginsn_src_print (src); |
576 | 0 | str_size += snprintf (ginsn_str + str_size, GINSN_LISTING_LEN - str_size, |
577 | 0 | " %s", src_buf); |
578 | 0 | free (src_buf); |
579 | 0 | gas_assert (str_size >= 0 && str_size < GINSN_LISTING_LEN); |
580 | | |
581 | | /* src 2. */ |
582 | 0 | src = ginsn_get_src2 (ginsn); |
583 | 0 | src_buf = ginsn_src_print (src); |
584 | 0 | str_size += snprintf (ginsn_str + str_size, GINSN_LISTING_LEN - str_size, |
585 | 0 | "%s", src_buf); |
586 | 0 | free (src_buf); |
587 | 0 | gas_assert (str_size >= 0 && str_size < GINSN_LISTING_LEN); |
588 | | |
589 | | /* dst. */ |
590 | 0 | dst = ginsn_get_dst (ginsn); |
591 | 0 | char *dst_buf = ginsn_dst_print (dst); |
592 | 0 | str_size += snprintf (ginsn_str + str_size, GINSN_LISTING_LEN - str_size, |
593 | 0 | "%s", dst_buf); |
594 | 0 | free (dst_buf); |
595 | |
|
596 | 0 | end: |
597 | 0 | gas_assert (str_size >= 0 && str_size < GINSN_LISTING_LEN); |
598 | 0 | return ginsn_str; |
599 | 0 | } |
600 | | |
601 | | static void |
602 | | gbb_cleanup (gbbS **bbp) |
603 | 0 | { |
604 | 0 | gbbS *bb = NULL; |
605 | |
|
606 | 0 | if (!bbp && !*bbp) |
607 | 0 | return; |
608 | | |
609 | 0 | bb = *bbp; |
610 | |
|
611 | 0 | if (bb->entry_state) |
612 | 0 | { |
613 | 0 | free (bb->entry_state); |
614 | 0 | bb->entry_state = NULL; |
615 | 0 | } |
616 | 0 | if (bb->exit_state) |
617 | 0 | { |
618 | 0 | free (bb->exit_state); |
619 | 0 | bb->exit_state = NULL; |
620 | 0 | } |
621 | 0 | free (bb); |
622 | 0 | *bbp = NULL; |
623 | 0 | } |
624 | | |
625 | | /* Add an edge from the source bb FROM_BB to the sink bb TO_BB. */ |
626 | | |
627 | | static void |
628 | | bb_add_edge (gbbS* from_bb, gbbS *to_bb) |
629 | 0 | { |
630 | 0 | gedgeS *tmpedge = NULL; |
631 | 0 | gedgeS *gedge; |
632 | 0 | bool exists = false; |
633 | |
|
634 | 0 | if (!from_bb || !to_bb) |
635 | 0 | return; |
636 | | |
637 | | /* Create a new edge object. */ |
638 | 0 | gedge = XCNEW (gedgeS); |
639 | 0 | gedge->dst_bb = to_bb; |
640 | 0 | gedge->next = NULL; |
641 | 0 | gedge->visited = false; |
642 | | |
643 | | /* Add it in. */ |
644 | 0 | if (from_bb->out_gedges == NULL) |
645 | 0 | { |
646 | 0 | from_bb->out_gedges = gedge; |
647 | 0 | from_bb->num_out_gedges++; |
648 | 0 | } |
649 | 0 | else |
650 | 0 | { |
651 | | /* Get the head of the list. */ |
652 | 0 | tmpedge = from_bb->out_gedges; |
653 | 0 | while (tmpedge) |
654 | 0 | { |
655 | | /* Do not add duplicate edges. Duplicated edges will cause unwanted |
656 | | failures in the forward and backward passes for SCFI. */ |
657 | 0 | if (tmpedge->dst_bb == to_bb) |
658 | 0 | { |
659 | 0 | exists = true; |
660 | 0 | break; |
661 | 0 | } |
662 | 0 | if (tmpedge->next) |
663 | 0 | tmpedge = tmpedge->next; |
664 | 0 | else |
665 | 0 | break; |
666 | 0 | } |
667 | |
|
668 | 0 | if (!exists) |
669 | 0 | { |
670 | 0 | tmpedge->next = gedge; |
671 | 0 | from_bb->num_out_gedges++; |
672 | 0 | } |
673 | 0 | else |
674 | 0 | free (gedge); |
675 | 0 | } |
676 | 0 | } |
677 | | |
678 | | static void |
679 | | cfg_add_bb (gcfgS *gcfg, gbbS *gbb) |
680 | 0 | { |
681 | 0 | gbbS *last_bb = NULL; |
682 | |
|
683 | 0 | if (!gcfg->root_bb) |
684 | 0 | gcfg->root_bb = gbb; |
685 | 0 | else |
686 | 0 | { |
687 | 0 | last_bb = gcfg->root_bb; |
688 | 0 | while (last_bb->next) |
689 | 0 | last_bb = last_bb->next; |
690 | |
|
691 | 0 | last_bb->next = gbb; |
692 | 0 | } |
693 | 0 | gcfg->num_gbbs++; |
694 | |
|
695 | 0 | gbb->id = gcfg->num_gbbs; |
696 | 0 | } |
697 | | |
698 | | static gbbS * |
699 | | add_bb_at_ginsn (const symbolS *func, gcfgS *gcfg, ginsnS *ginsn, gbbS *prev_bb, |
700 | | int *errp); |
701 | | |
702 | | /* Return the already existing basic block (if present), which begins with |
703 | | GINSN, in the given GCFG. Return NULL otherwise. */ |
704 | | |
705 | | static gbbS * |
706 | | find_bb (gcfgS *gcfg, ginsnS *ginsn) |
707 | 0 | { |
708 | 0 | gbbS *found_bb = NULL; |
709 | 0 | gbbS *gbb = NULL; |
710 | |
|
711 | 0 | if (!ginsn) |
712 | 0 | return found_bb; |
713 | | |
714 | 0 | if (ginsn->visited) |
715 | 0 | { |
716 | 0 | cfg_for_each_bb (gcfg, gbb) |
717 | 0 | { |
718 | 0 | if (gbb->first_ginsn == ginsn) |
719 | 0 | { |
720 | 0 | found_bb = gbb; |
721 | 0 | break; |
722 | 0 | } |
723 | 0 | } |
724 | | /* Must be found because ginsn is visited. */ |
725 | 0 | gas_assert (found_bb); |
726 | 0 | } |
727 | | |
728 | 0 | return found_bb; |
729 | 0 | } |
730 | | |
731 | | /* Get the basic block starting at GINSN in the GCFG. |
732 | | |
733 | | If not already present, the function will make one, while adding an edge |
734 | | from the PREV_BB to it. */ |
735 | | |
736 | | static gbbS * |
737 | | find_or_make_bb (const symbolS *func, gcfgS *gcfg, ginsnS *ginsn, gbbS *prev_bb, |
738 | | int *errp) |
739 | 0 | { |
740 | 0 | gbbS *found_bb = NULL; |
741 | |
|
742 | 0 | found_bb = find_bb (gcfg, ginsn); |
743 | 0 | if (!found_bb) |
744 | 0 | found_bb = add_bb_at_ginsn (func, gcfg, ginsn, prev_bb, errp); |
745 | |
|
746 | 0 | gas_assert (found_bb); |
747 | 0 | gas_assert (found_bb->first_ginsn == ginsn); |
748 | | |
749 | 0 | return found_bb; |
750 | 0 | } |
751 | | |
752 | | /* Add basic block(s) for all reachable, unvisited ginsns, starting from GINSN, |
753 | | to the given GCFG. Also add an edge from the PREV_BB to the root of the |
754 | | newly added basic block(s). |
755 | | |
756 | | This is a recursive function which returns the root of the added basic |
757 | | blocks. */ |
758 | | |
759 | | static gbbS * |
760 | | add_bb_at_ginsn (const symbolS *func, gcfgS *gcfg, ginsnS *ginsn, gbbS *prev_bb, |
761 | | int *errp) |
762 | 0 | { |
763 | 0 | gbbS *root_bb = NULL; |
764 | 0 | gbbS *current_bb = NULL; |
765 | 0 | ginsnS *target_ginsn = NULL; |
766 | 0 | const symbolS *taken_label; |
767 | | |
768 | | /* Create a new bb. N.B. The caller must ensure bb with this ginsn does not |
769 | | already exist. */ |
770 | 0 | gas_assert (!find_bb (gcfg, ginsn)); |
771 | 0 | root_bb = XCNEW (gbbS); |
772 | 0 | cfg_add_bb (gcfg, root_bb); |
773 | 0 | root_bb->first_ginsn = ginsn; |
774 | |
|
775 | 0 | current_bb = root_bb; |
776 | |
|
777 | 0 | while (ginsn) |
778 | 0 | { |
779 | | /* Skip these as they may be right after a GINSN_TYPE_RETURN. |
780 | | For GINSN_TYPE_RETURN, we have already considered that as |
781 | | end of bb, and a logical exit from function. */ |
782 | 0 | if (GINSN_F_FUNC_END_P (ginsn)) |
783 | 0 | { |
784 | | /* Dont mark them visited yet though, leaving the option of these |
785 | | being visited via other control flows as applicable. */ |
786 | 0 | ginsn = ginsn->next; |
787 | 0 | continue; |
788 | 0 | } |
789 | | |
790 | 0 | if (ginsn->visited) |
791 | 0 | { |
792 | | /* If the ginsn has been visited earlier, the bb must exist by now |
793 | | in the cfg. */ |
794 | 0 | prev_bb = current_bb; |
795 | 0 | current_bb = find_bb (gcfg, ginsn); |
796 | 0 | gas_assert (current_bb); |
797 | | /* Add edge from the prev_bb. */ |
798 | 0 | if (prev_bb) |
799 | 0 | bb_add_edge (prev_bb, current_bb); |
800 | 0 | break; |
801 | 0 | } |
802 | 0 | else if (current_bb && current_bb->first_ginsn != ginsn |
803 | 0 | && GINSN_F_USER_LABEL_P (ginsn)) |
804 | 0 | { |
805 | | /* Create new bb starting at ginsn for (user-defined) label. This is |
806 | | likely going to be a destination of a some control flow. */ |
807 | 0 | prev_bb = current_bb; |
808 | 0 | current_bb = find_or_make_bb (func, gcfg, ginsn, prev_bb, errp); |
809 | 0 | bb_add_edge (prev_bb, current_bb); |
810 | 0 | break; |
811 | 0 | } |
812 | | |
813 | 0 | if (current_bb == NULL) |
814 | 0 | { |
815 | 0 | current_bb = XCNEW (gbbS); |
816 | 0 | cfg_add_bb (gcfg, current_bb); |
817 | 0 | current_bb->first_ginsn = ginsn; |
818 | | /* Add edge for the Not Taken, or Fall-through path. */ |
819 | 0 | if (prev_bb) |
820 | 0 | bb_add_edge (prev_bb, current_bb); |
821 | 0 | } |
822 | |
|
823 | 0 | ginsn->visited = true; |
824 | 0 | current_bb->num_ginsns++; |
825 | 0 | current_bb->last_ginsn = ginsn; |
826 | | |
827 | | /* Note that BB is _not_ split on ginsn of type GINSN_TYPE_CALL. */ |
828 | 0 | if (ginsn->type == GINSN_TYPE_JUMP |
829 | 0 | || ginsn->type == GINSN_TYPE_JUMP_COND |
830 | 0 | || ginsn->type == GINSN_TYPE_RETURN) |
831 | 0 | { |
832 | | /* Indirect jumps must not be seen here. The caller must have |
833 | | already checked for that. */ |
834 | 0 | gas_assert (!ginsn_indirect_jump_p (ginsn)); |
835 | | |
836 | | /* Handle direct jumps. For unconditional direct jumps, where the |
837 | | target is not local to the function, treat them later as similar |
838 | | to an exit from function (in the else block). */ |
839 | 0 | if (ginsn->type == GINSN_TYPE_JUMP_COND |
840 | 0 | || ginsn_direct_local_jump_p (ginsn)) |
841 | 0 | { |
842 | 0 | gas_assert (ginsn->src[0].type == GINSN_SRC_SYMBOL); |
843 | 0 | taken_label = ginsn->src[0].sym; |
844 | 0 | gas_assert (taken_label); |
845 | | |
846 | | /* Preserve the prev_bb to be the source bb as we are going to |
847 | | follow the taken path of the conditional branch soon. */ |
848 | 0 | prev_bb = current_bb; |
849 | | |
850 | | /* Follow the target on the taken path. */ |
851 | 0 | target_ginsn = label_ginsn_map_find (taken_label); |
852 | | /* Add the bb for the target of the taken branch. */ |
853 | 0 | if (target_ginsn) |
854 | 0 | { |
855 | 0 | current_bb = find_or_make_bb (func, gcfg, target_ginsn, |
856 | 0 | prev_bb, errp); |
857 | 0 | gas_assert (prev_bb); |
858 | 0 | bb_add_edge (prev_bb, current_bb); |
859 | 0 | current_bb = NULL; |
860 | 0 | } |
861 | 0 | else |
862 | 0 | { |
863 | 0 | *errp = GCFG_JLABEL_NOT_PRESENT; |
864 | 0 | as_warn_where (ginsn->file, ginsn->line, |
865 | 0 | _("missing label '%s' in func '%s' may result in imprecise cfg"), |
866 | 0 | S_GET_NAME (taken_label), S_GET_NAME (func)); |
867 | 0 | } |
868 | | |
869 | 0 | if (ginsn->type == GINSN_TYPE_JUMP_COND) |
870 | 0 | { |
871 | | /* Add the bb for the fall through path. */ |
872 | 0 | current_bb = find_or_make_bb (func, gcfg, ginsn->next, |
873 | 0 | prev_bb, errp); |
874 | 0 | gas_assert (prev_bb); |
875 | 0 | bb_add_edge (prev_bb, current_bb); |
876 | 0 | current_bb = NULL; |
877 | 0 | } |
878 | 0 | else |
879 | 0 | { |
880 | | /* Unconditional jump. Current BB has been processed. */ |
881 | 0 | current_bb = NULL; |
882 | | /* We'll come back to the ginsns following these (local) |
883 | | unconditional jmps from another path if they are indeed |
884 | | reachable code. */ |
885 | 0 | break; |
886 | 0 | } |
887 | 0 | } |
888 | 0 | else |
889 | 0 | { |
890 | 0 | gas_assert (ginsn->type == GINSN_TYPE_RETURN |
891 | 0 | || (ginsn->type == GINSN_TYPE_JUMP |
892 | 0 | && !ginsn_direct_local_jump_p (ginsn))); |
893 | | /* Current BB has been processed. */ |
894 | 0 | current_bb = NULL; |
895 | | |
896 | | /* We'll come back to the ginsns following GINSN_TYPE_RETURN or |
897 | | other (non-local) unconditional jmps from another path if they |
898 | | are indeed reachable code. */ |
899 | 0 | break; |
900 | 0 | } |
901 | 0 | } |
902 | | |
903 | 0 | ginsn = ginsn->next; |
904 | 0 | } |
905 | | |
906 | 0 | return root_bb; |
907 | 0 | } |
908 | | |
909 | | static int |
910 | | gbbs_compare (const void *v1, const void *v2) |
911 | 0 | { |
912 | 0 | const gbbS *bb1 = *(const gbbS **) v1; |
913 | 0 | const gbbS *bb2 = *(const gbbS **) v2; |
914 | |
|
915 | 0 | if (bb1->first_ginsn->id < bb2->first_ginsn->id) |
916 | 0 | return -1; |
917 | 0 | else if (bb1->first_ginsn->id > bb2->first_ginsn->id) |
918 | 0 | return 1; |
919 | 0 | else if (bb1->first_ginsn->id == bb2->first_ginsn->id) |
920 | 0 | return 0; |
921 | | |
922 | 0 | return 0; |
923 | 0 | } |
924 | | |
925 | | /* Synthesize DWARF CFI and emit it. */ |
926 | | |
927 | | static int |
928 | | ginsn_pass_execute_scfi (const symbolS *func, gcfgS *gcfg, gbbS *root_bb) |
929 | 0 | { |
930 | 0 | int err = scfi_synthesize_dw2cfi (func, gcfg, root_bb); |
931 | 0 | if (!err) |
932 | 0 | scfi_emit_dw2cfi (func); |
933 | |
|
934 | 0 | return err; |
935 | 0 | } |
936 | | |
937 | | /* Traverse the list of ginsns for the function and warn if some |
938 | | ginsns are not visited. |
939 | | |
940 | | FIXME - this code assumes the caller has already performed a pass over |
941 | | ginsns such that the reachable ginsns are already marked. Revisit this - we |
942 | | should ideally make this pass self-sufficient. */ |
943 | | |
944 | | static int |
945 | | ginsn_pass_warn_unreachable_code (const symbolS *func, |
946 | | gcfgS *gcfg ATTRIBUTE_UNUSED, |
947 | | ginsnS *root_ginsn) |
948 | 0 | { |
949 | 0 | ginsnS *ginsn; |
950 | 0 | bool unreach_p = false; |
951 | |
|
952 | 0 | if (!gcfg || !func || !root_ginsn) |
953 | 0 | return 0; |
954 | | |
955 | 0 | ginsn = root_ginsn; |
956 | |
|
957 | 0 | while (ginsn) |
958 | 0 | { |
959 | | /* Some ginsns of type GINSN_TYPE_SYMBOL remain unvisited. Some |
960 | | may even be excluded from the CFG as they are not reachable, given |
961 | | their function, e.g., user labels after return machine insn. */ |
962 | 0 | if (!ginsn->visited |
963 | 0 | && !GINSN_F_FUNC_END_P (ginsn) |
964 | 0 | && !GINSN_F_USER_LABEL_P (ginsn)) |
965 | 0 | { |
966 | 0 | unreach_p = true; |
967 | 0 | break; |
968 | 0 | } |
969 | 0 | ginsn = ginsn->next; |
970 | 0 | } |
971 | |
|
972 | 0 | if (unreach_p) |
973 | 0 | as_warn_where (ginsn->file, ginsn->line, |
974 | 0 | _("GINSN: found unreachable code in func '%s'"), |
975 | 0 | S_GET_NAME (func)); |
976 | |
|
977 | 0 | return unreach_p; |
978 | 0 | } |
979 | | |
980 | | void |
981 | | gcfg_get_bbs_in_prog_order (gcfgS *gcfg, gbbS **prog_order_bbs) |
982 | 0 | { |
983 | 0 | uint64_t i = 0; |
984 | 0 | gbbS *gbb; |
985 | |
|
986 | 0 | if (!prog_order_bbs) |
987 | 0 | return; |
988 | | |
989 | 0 | cfg_for_each_bb (gcfg, gbb) |
990 | 0 | { |
991 | 0 | gas_assert (i < gcfg->num_gbbs); |
992 | 0 | prog_order_bbs[i++] = gbb; |
993 | 0 | } |
994 | | |
995 | 0 | qsort (prog_order_bbs, gcfg->num_gbbs, sizeof (gbbS *), gbbs_compare); |
996 | 0 | } |
997 | | |
998 | | /* Build the control flow graph for the ginsns of the function. |
999 | | |
1000 | | It is important that the target adds an appropriate ginsn: |
1001 | | - GINSN_TYPE_JUMP, |
1002 | | - GINSN_TYPE_JUMP_COND, |
1003 | | - GINSN_TYPE_CALL, |
1004 | | - GINSN_TYPE_RET |
1005 | | at the associated points in the function. The correctness of the CFG |
1006 | | depends on the accuracy of these 'change of flow instructions'. */ |
1007 | | |
1008 | | gcfgS * |
1009 | | gcfg_build (const symbolS *func, int *errp) |
1010 | 0 | { |
1011 | 0 | gcfgS *gcfg; |
1012 | 0 | ginsnS *first_ginsn; |
1013 | |
|
1014 | 0 | gcfg = XCNEW (gcfgS); |
1015 | 0 | first_ginsn = frchain_now->frch_ginsn_data->gins_rootP; |
1016 | 0 | add_bb_at_ginsn (func, gcfg, first_ginsn, NULL /* prev_bb. */, errp); |
1017 | |
|
1018 | 0 | return gcfg; |
1019 | 0 | } |
1020 | | |
1021 | | void |
1022 | | gcfg_cleanup (gcfgS **gcfgp) |
1023 | 0 | { |
1024 | 0 | gcfgS *cfg; |
1025 | 0 | gbbS *bb, *next_bb; |
1026 | 0 | gedgeS *edge, *next_edge; |
1027 | |
|
1028 | 0 | if (!gcfgp || !*gcfgp) |
1029 | 0 | return; |
1030 | | |
1031 | 0 | cfg = *gcfgp; |
1032 | 0 | bb = gcfg_get_rootbb (cfg); |
1033 | |
|
1034 | 0 | while (bb) |
1035 | 0 | { |
1036 | 0 | next_bb = bb->next; |
1037 | | |
1038 | | /* Cleanup all the edges. */ |
1039 | 0 | edge = bb->out_gedges; |
1040 | 0 | while (edge) |
1041 | 0 | { |
1042 | 0 | next_edge = edge->next; |
1043 | 0 | free (edge); |
1044 | 0 | edge = next_edge; |
1045 | 0 | } |
1046 | |
|
1047 | 0 | gbb_cleanup (&bb); |
1048 | 0 | bb = next_bb; |
1049 | 0 | } |
1050 | |
|
1051 | 0 | free (cfg); |
1052 | 0 | *gcfgp = NULL; |
1053 | 0 | } |
1054 | | |
1055 | | gbbS * |
1056 | | gcfg_get_rootbb (gcfgS *gcfg) |
1057 | 0 | { |
1058 | 0 | gbbS *rootbb = NULL; |
1059 | |
|
1060 | 0 | if (!gcfg || !gcfg->num_gbbs) |
1061 | 0 | return NULL; |
1062 | | |
1063 | 0 | rootbb = gcfg->root_bb; |
1064 | |
|
1065 | 0 | return rootbb; |
1066 | 0 | } |
1067 | | |
1068 | | void |
1069 | | gcfg_print (const gcfgS *gcfg, FILE *outfile) |
1070 | 0 | { |
1071 | 0 | gbbS *gbb = NULL; |
1072 | 0 | gedgeS *gedge = NULL; |
1073 | 0 | uint64_t total_ginsns = 0; |
1074 | |
|
1075 | 0 | cfg_for_each_bb(gcfg, gbb) |
1076 | 0 | { |
1077 | 0 | fprintf (outfile, "BB [%" PRIu64 "] with num insns: %" PRIu64, |
1078 | 0 | gbb->id, gbb->num_ginsns); |
1079 | 0 | fprintf (outfile, " [insns: %u to %u]\n", |
1080 | 0 | gbb->first_ginsn->line, gbb->last_ginsn->line); |
1081 | 0 | total_ginsns += gbb->num_ginsns; |
1082 | 0 | bb_for_each_edge(gbb, gedge) |
1083 | 0 | fprintf (outfile, " outgoing edge to %" PRIu64 "\n", |
1084 | 0 | gedge->dst_bb->id); |
1085 | 0 | } |
1086 | 0 | fprintf (outfile, "\nTotal ginsns in all GBBs = %" PRIu64 "\n", |
1087 | 0 | total_ginsns); |
1088 | 0 | } |
1089 | | |
1090 | | void |
1091 | | frch_ginsn_data_init (const symbolS *func, symbolS *start_addr, |
1092 | | enum ginsn_gen_mode gmode) |
1093 | 0 | { |
1094 | | /* FIXME - error out if prev object is not free'd ? */ |
1095 | 0 | frchain_now->frch_ginsn_data = XCNEW (struct frch_ginsn_data); |
1096 | |
|
1097 | 0 | frchain_now->frch_ginsn_data->mode = gmode; |
1098 | | /* Annotate with the current function symbol. */ |
1099 | 0 | frchain_now->frch_ginsn_data->func = func; |
1100 | | /* Create a new start address symbol now. */ |
1101 | 0 | frchain_now->frch_ginsn_data->start_addr = start_addr; |
1102 | | /* Assume the set of ginsn are apt for CFG creation, by default. */ |
1103 | 0 | frchain_now->frch_ginsn_data->gcfg_apt_p = true; |
1104 | |
|
1105 | 0 | frchain_now->frch_ginsn_data->label_ginsn_map = str_htab_create (); |
1106 | 0 | } |
1107 | | |
1108 | | void |
1109 | | frch_ginsn_data_cleanup (void) |
1110 | 0 | { |
1111 | 0 | ginsnS *ginsn = NULL; |
1112 | 0 | ginsnS *next_ginsn = NULL; |
1113 | |
|
1114 | 0 | ginsn = frchain_now->frch_ginsn_data->gins_rootP; |
1115 | 0 | while (ginsn) |
1116 | 0 | { |
1117 | 0 | next_ginsn = ginsn->next; |
1118 | 0 | ginsn_cleanup (&ginsn); |
1119 | 0 | ginsn = next_ginsn; |
1120 | 0 | } |
1121 | |
|
1122 | 0 | if (frchain_now->frch_ginsn_data->label_ginsn_map) |
1123 | 0 | htab_delete (frchain_now->frch_ginsn_data->label_ginsn_map); |
1124 | |
|
1125 | 0 | free (frchain_now->frch_ginsn_data); |
1126 | 0 | frchain_now->frch_ginsn_data = NULL; |
1127 | 0 | } |
1128 | | |
1129 | | /* Append GINSN to the list of ginsns for the current function being |
1130 | | assembled. */ |
1131 | | |
1132 | | int |
1133 | | frch_ginsn_data_append (ginsnS *ginsn) |
1134 | 0 | { |
1135 | 0 | ginsnS *last = NULL; |
1136 | 0 | ginsnS *temp = NULL; |
1137 | 0 | uint64_t id = 0; |
1138 | |
|
1139 | 0 | if (!ginsn) |
1140 | 0 | return 1; |
1141 | | |
1142 | 0 | if (frchain_now->frch_ginsn_data->gins_lastP) |
1143 | 0 | id = frchain_now->frch_ginsn_data->gins_lastP->id; |
1144 | | |
1145 | | /* Do the necessary preprocessing on the set of input GINSNs: |
1146 | | - Update each ginsn with its ID. |
1147 | | While you iterate, also keep gcfg_apt_p updated by checking whether any |
1148 | | ginsn is inappropriate for GCFG creation. */ |
1149 | 0 | temp = ginsn; |
1150 | 0 | while (temp) |
1151 | 0 | { |
1152 | 0 | temp->id = ++id; |
1153 | |
|
1154 | 0 | if (ginsn_indirect_jump_p (temp)) |
1155 | 0 | frchain_now->frch_ginsn_data->gcfg_apt_p = false; |
1156 | |
|
1157 | 0 | if (listing & LISTING_GINSN_SCFI) |
1158 | 0 | listing_newline (ginsn_print (temp)); |
1159 | | |
1160 | | /* The input GINSN may be a linked list of multiple ginsns chained |
1161 | | together. Find the last ginsn in the input chain of ginsns. */ |
1162 | 0 | last = temp; |
1163 | |
|
1164 | 0 | temp = temp->next; |
1165 | 0 | } |
1166 | | |
1167 | | /* Link in the ginsn to the tail. */ |
1168 | 0 | if (!frchain_now->frch_ginsn_data->gins_rootP) |
1169 | 0 | frchain_now->frch_ginsn_data->gins_rootP = ginsn; |
1170 | 0 | else |
1171 | 0 | ginsn_link_next (frchain_now->frch_ginsn_data->gins_lastP, ginsn); |
1172 | |
|
1173 | 0 | frchain_now->frch_ginsn_data->gins_lastP = last; |
1174 | |
|
1175 | 0 | return 0; |
1176 | 0 | } |
1177 | | |
1178 | | enum ginsn_gen_mode |
1179 | | frch_ginsn_gen_mode (void) |
1180 | 0 | { |
1181 | 0 | enum ginsn_gen_mode gmode = GINSN_GEN_NONE; |
1182 | |
|
1183 | 0 | if (frchain_now->frch_ginsn_data) |
1184 | 0 | gmode = frchain_now->frch_ginsn_data->mode; |
1185 | |
|
1186 | 0 | return gmode; |
1187 | 0 | } |
1188 | | |
1189 | | int |
1190 | | ginsn_data_begin (const symbolS *func) |
1191 | 0 | { |
1192 | 0 | ginsnS *ginsn; |
1193 | | |
1194 | | /* The previous block of asm must have been processed by now. */ |
1195 | 0 | if (frchain_now->frch_ginsn_data) |
1196 | 0 | as_bad (_("GINSN process for prev func not done")); |
1197 | | |
1198 | | /* FIXME - hard code the mode to GINSN_GEN_SCFI. |
1199 | | This can be changed later when other passes on ginsns are formalised. */ |
1200 | 0 | frch_ginsn_data_init (func, symbol_temp_new_now (), GINSN_GEN_SCFI); |
1201 | | |
1202 | | /* Create and insert ginsn with function begin marker. */ |
1203 | 0 | ginsn = ginsn_new_symbol_func_begin (func); |
1204 | 0 | frch_ginsn_data_append (ginsn); |
1205 | |
|
1206 | 0 | return 0; |
1207 | 0 | } |
1208 | | |
1209 | | int |
1210 | | ginsn_data_end (const symbolS *label) |
1211 | 0 | { |
1212 | 0 | ginsnS *ginsn; |
1213 | 0 | gbbS *root_bb; |
1214 | 0 | gcfgS *gcfg = NULL; |
1215 | 0 | const symbolS *func; |
1216 | 0 | int err = 0; |
1217 | |
|
1218 | 0 | if (!frchain_now->frch_ginsn_data) |
1219 | 0 | return err; |
1220 | | |
1221 | | /* Insert Function end marker. */ |
1222 | 0 | ginsn = ginsn_new_symbol_func_end (label); |
1223 | 0 | frch_ginsn_data_append (ginsn); |
1224 | |
|
1225 | 0 | func = frchain_now->frch_ginsn_data->func; |
1226 | | |
1227 | | /* Build the cfg of ginsn(s) of the function. */ |
1228 | 0 | if (!frchain_now->frch_ginsn_data->gcfg_apt_p) |
1229 | 0 | { |
1230 | 0 | as_bad (_("untraceable control flow for func '%s'"), |
1231 | 0 | S_GET_NAME (func)); |
1232 | 0 | goto end; |
1233 | 0 | } |
1234 | | |
1235 | 0 | gcfg = gcfg_build (func, &err); |
1236 | |
|
1237 | 0 | root_bb = gcfg_get_rootbb (gcfg); |
1238 | 0 | if (!root_bb) |
1239 | 0 | { |
1240 | 0 | as_bad (_("Bad cfg of ginsn of func '%s'"), S_GET_NAME (func)); |
1241 | 0 | goto end; |
1242 | 0 | } |
1243 | | |
1244 | | /* Execute the desired passes on ginsns. */ |
1245 | 0 | err = ginsn_pass_execute_scfi (func, gcfg, root_bb); |
1246 | 0 | if (err) |
1247 | 0 | goto end; |
1248 | | |
1249 | | /* Other passes, e.g., warn for unreachable code can be enabled too. */ |
1250 | 0 | ginsn = frchain_now->frch_ginsn_data->gins_rootP; |
1251 | 0 | err = ginsn_pass_warn_unreachable_code (func, gcfg, ginsn); |
1252 | |
|
1253 | 0 | end: |
1254 | 0 | if (gcfg) |
1255 | 0 | gcfg_cleanup (&gcfg); |
1256 | 0 | frch_ginsn_data_cleanup (); |
1257 | |
|
1258 | 0 | return err; |
1259 | 0 | } |
1260 | | |
1261 | | /* Add GINSN_TYPE_SYMBOL type ginsn for user-defined labels. These may be |
1262 | | branch targets, and hence are necessary for control flow graph. */ |
1263 | | |
1264 | | void |
1265 | | ginsn_frob_label (const symbolS *label) |
1266 | 0 | { |
1267 | 0 | ginsnS *label_ginsn; |
1268 | 0 | const char *file; |
1269 | 0 | unsigned int line; |
1270 | |
|
1271 | 0 | if (frchain_now->frch_ginsn_data) |
1272 | 0 | { |
1273 | | /* PS: Note how we keep the actual LABEL symbol as ginsn->sym. |
1274 | | Take care to avoid inadvertent updates or cleanups of symbols. */ |
1275 | 0 | label_ginsn = ginsn_new_symbol_user_label (label); |
1276 | | /* Keep the location updated. */ |
1277 | 0 | file = as_where (&line); |
1278 | 0 | ginsn_set_file_line (label_ginsn, file, line); |
1279 | |
|
1280 | 0 | frch_ginsn_data_append (label_ginsn); |
1281 | |
|
1282 | 0 | label_ginsn_map_insert (label, label_ginsn); |
1283 | 0 | } |
1284 | 0 | } |
1285 | | |
1286 | | const symbolS * |
1287 | | ginsn_data_func_symbol (void) |
1288 | 0 | { |
1289 | 0 | const symbolS *func = NULL; |
1290 | |
|
1291 | 0 | if (frchain_now->frch_ginsn_data) |
1292 | 0 | func = frchain_now->frch_ginsn_data->func; |
1293 | |
|
1294 | 0 | return func; |
1295 | 0 | } |
1296 | | |
1297 | | #else |
1298 | | |
1299 | | int |
1300 | | ginsn_data_begin (const symbolS *func ATTRIBUTE_UNUSED) |
1301 | | { |
1302 | | as_bad (_("ginsn unsupported for target")); |
1303 | | return 1; |
1304 | | } |
1305 | | |
1306 | | int |
1307 | | ginsn_data_end (const symbolS *label ATTRIBUTE_UNUSED) |
1308 | | { |
1309 | | as_bad (_("ginsn unsupported for target")); |
1310 | | return 1; |
1311 | | } |
1312 | | |
1313 | | void |
1314 | | ginsn_frob_label (const symbolS *sym ATTRIBUTE_UNUSED) |
1315 | | { |
1316 | | return; |
1317 | | } |
1318 | | |
1319 | | const symbolS * |
1320 | | ginsn_data_func_symbol (void) |
1321 | | { |
1322 | | return NULL; |
1323 | | } |
1324 | | |
1325 | | #endif /* TARGET_USE_GINSN. */ |