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