]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/symtab-thunks.cc
Move thunks out of cgraph_node
[thirdparty/gcc.git] / gcc / symtab-thunks.cc
CommitLineData
67f3791f
JH
1/* Support for thunks in symbol table.
2 Copyright (C) 2003-2020 Free Software Foundation, Inc.
3 Contributed by Jan Hubicka
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 3, or (at your option) any later
10version.
11
12GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License
18along with GCC; see the file COPYING3. If not see
19<http://www.gnu.org/licenses/>. */
20
21#include "config.h"
22#include "system.h"
23#include "coretypes.h"
24#include "backend.h"
25#include "tree.h"
26#include "gimple.h"
27#include "predict.h"
28#include "target.h"
29#include "rtl.h"
30#include "alloc-pool.h"
31#include "cgraph.h"
32#include "symbol-summary.h"
33#include "symtab-thunks.h"
34#include "lto-streamer.h"
35#include "fold-const.h"
36#include "gimple-iterator.h"
37#include "stor-layout.h"
38#include "gimplify-me.h"
39#include "varasm.h"
40#include "output.h"
41#include "cfg.h"
42#include "cfghooks.h"
43#include "gimple-ssa.h"
44#include "gimple-fold.h"
45#include "cfgloop.h"
46#include "tree-into-ssa.h"
47#include "tree-cfg.h"
48#include "cfgcleanup.h"
49#include "tree-pass.h"
50#include "data-streamer.h"
51#include "langhooks.h"
52
53/* Used for vtable lookup in thunk adjusting. */
54static GTY (()) tree vtable_entry_type;
55
56namespace {
57
58/* Function summary for thunk_infos. */
59class GTY((user)) thunk_infos_t: public function_summary <thunk_info *>
60{
61public:
62 thunk_infos_t (symbol_table *table, bool ggc):
63 function_summary<thunk_info *> (table, ggc) { }
64
65 /* Hook that is called by summary when a node is duplicated. */
66 virtual void duplicate (cgraph_node *node,
67 cgraph_node *node2,
68 thunk_info *data,
69 thunk_info *data2);
70};
71
72/* Duplication hook. */
73void
74thunk_infos_t::duplicate (cgraph_node *, cgraph_node *,
75 thunk_info *src, thunk_info *dst)
76{
77 *dst = *src;
78}
79
80} /* anon namespace */
81
82/* Return thunk_info possibly creating new one. */
83thunk_info *
84thunk_info::get_create (cgraph_node *node)
85{
86 if (!symtab->m_thunks)
87 {
88 symtab->m_thunks
89 = new (ggc_alloc_no_dtor <thunk_infos_t> ())
90 thunk_infos_t (symtab, true);
91 symtab->m_thunks->disable_insertion_hook ();
92 }
93 return symtab->m_thunks->get_create (node);
94}
95
96/* Stream out THIS to OB. */
97void
98thunk_info::stream_out (lto_simple_output_block *ob)
99{
100 streamer_write_uhwi_stream
101 (ob->main_stream,
102 1 + (this_adjusting != 0) * 2
103 + (virtual_offset_p != 0) * 4);
104 streamer_write_uhwi_stream (ob->main_stream, fixed_offset);
105 streamer_write_uhwi_stream (ob->main_stream, virtual_value);
106 streamer_write_uhwi_stream (ob->main_stream, indirect_offset);
107}
108
109/* Stream in THIS from IB. */
110void
111thunk_info::stream_in (class lto_input_block *ib)
112{
113 int type = streamer_read_uhwi (ib);
114 fixed_offset = streamer_read_uhwi (ib);
115 virtual_value = streamer_read_uhwi (ib);
116 indirect_offset = streamer_read_uhwi (ib);
117
118 this_adjusting = (type & 2);
119 virtual_offset_p = (type & 4);
120}
121
122/* Dump THIS to F. */
123void
124thunk_info::dump (FILE *f)
125{
126 if (alias)
127 fprintf (f, " of %s (asm:%s)",
128 lang_hooks.decl_printable_name (alias, 2),
129 IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (alias)));
130 fprintf (f, " fixed offset %i virtual value %i indirect_offset %i "
131 "has virtual offset %i\n",
132 (int)fixed_offset,
133 (int)virtual_value,
134 (int)indirect_offset,
135 (int)virtual_offset_p);
136}
137
138/* Hash THIS. */
139hashval_t
140thunk_info::hash ()
141{
142 inchash::hash hstate;
143 hstate.add_hwi (fixed_offset);
144 hstate.add_hwi (virtual_value);
145 hstate.add_flag (this_adjusting);
146 hstate.add_flag (virtual_offset_p);
147 return hstate.end ();
148}
149
150/* Adjust PTR by the constant FIXED_OFFSET, by the vtable offset indicated by
151 VIRTUAL_OFFSET, and by the indirect offset indicated by INDIRECT_OFFSET, if
152 it is non-null. THIS_ADJUSTING is nonzero for a this adjusting thunk and zero
153 for a result adjusting thunk. */
154tree
155thunk_adjust (gimple_stmt_iterator * bsi,
156 tree ptr, bool this_adjusting,
157 HOST_WIDE_INT fixed_offset, tree virtual_offset,
158 HOST_WIDE_INT indirect_offset)
159{
160 gassign *stmt;
161 tree ret;
162
163 if (this_adjusting
164 && fixed_offset != 0)
165 {
166 stmt = gimple_build_assign
167 (ptr, fold_build_pointer_plus_hwi_loc (input_location,
168 ptr,
169 fixed_offset));
170 gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
171 }
172
173 if (!vtable_entry_type && (virtual_offset || indirect_offset != 0))
174 {
175 tree vfunc_type = make_node (FUNCTION_TYPE);
176 TREE_TYPE (vfunc_type) = integer_type_node;
177 TYPE_ARG_TYPES (vfunc_type) = NULL_TREE;
178 layout_type (vfunc_type);
179
180 vtable_entry_type = build_pointer_type (vfunc_type);
181 }
182
183 /* If there's a virtual offset, look up that value in the vtable and
184 adjust the pointer again. */
185 if (virtual_offset)
186 {
187 tree vtabletmp;
188 tree vtabletmp2;
189 tree vtabletmp3;
190
191 vtabletmp = create_tmp_reg
192 (build_pointer_type
193 (build_pointer_type (vtable_entry_type)), "vptr");
194
195 /* The vptr is always at offset zero in the object. */
196 stmt = gimple_build_assign (vtabletmp,
197 build1 (NOP_EXPR, TREE_TYPE (vtabletmp),
198 ptr));
199 gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
200
201 /* Form the vtable address. */
202 vtabletmp2 = create_tmp_reg (TREE_TYPE (TREE_TYPE (vtabletmp)),
203 "vtableaddr");
204 stmt = gimple_build_assign (vtabletmp2,
205 build_simple_mem_ref (vtabletmp));
206 gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
207
208 /* Find the entry with the vcall offset. */
209 stmt = gimple_build_assign (vtabletmp2,
210 fold_build_pointer_plus_loc (input_location,
211 vtabletmp2,
212 virtual_offset));
213 gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
214
215 /* Get the offset itself. */
216 vtabletmp3 = create_tmp_reg (TREE_TYPE (TREE_TYPE (vtabletmp2)),
217 "vcalloffset");
218 stmt = gimple_build_assign (vtabletmp3,
219 build_simple_mem_ref (vtabletmp2));
220 gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
221
222 /* Adjust the `this' pointer. */
223 ptr = fold_build_pointer_plus_loc (input_location, ptr, vtabletmp3);
224 ptr = force_gimple_operand_gsi (bsi, ptr, true, NULL_TREE, false,
225 GSI_CONTINUE_LINKING);
226 }
227
228 /* Likewise for an offset that is stored in the object that contains the
229 vtable. */
230 if (indirect_offset != 0)
231 {
232 tree offset_ptr, offset_tree;
233
234 /* Get the address of the offset. */
235 offset_ptr
236 = create_tmp_reg (build_pointer_type
237 (build_pointer_type (vtable_entry_type)),
238 "offset_ptr");
239 stmt = gimple_build_assign (offset_ptr,
240 build1 (NOP_EXPR, TREE_TYPE (offset_ptr),
241 ptr));
242 gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
243
244 stmt = gimple_build_assign
245 (offset_ptr,
246 fold_build_pointer_plus_hwi_loc (input_location, offset_ptr,
247 indirect_offset));
248 gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
249
250 /* Get the offset itself. */
251 offset_tree = create_tmp_reg (TREE_TYPE (TREE_TYPE (offset_ptr)),
252 "offset");
253 stmt = gimple_build_assign (offset_tree,
254 build_simple_mem_ref (offset_ptr));
255 gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
256
257 /* Adjust the `this' pointer. */
258 ptr = fold_build_pointer_plus_loc (input_location, ptr, offset_tree);
259 ptr = force_gimple_operand_gsi (bsi, ptr, true, NULL_TREE, false,
260 GSI_CONTINUE_LINKING);
261 }
262
263 if (!this_adjusting
264 && fixed_offset != 0)
265 /* Adjust the pointer by the constant. */
266 {
267 tree ptrtmp;
268
269 if (VAR_P (ptr))
270 ptrtmp = ptr;
271 else
272 {
273 ptrtmp = create_tmp_reg (TREE_TYPE (ptr), "ptr");
274 stmt = gimple_build_assign (ptrtmp, ptr);
275 gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
276 }
277 ptr = fold_build_pointer_plus_hwi_loc (input_location,
278 ptrtmp, fixed_offset);
279 }
280
281 /* Emit the statement and gimplify the adjustment expression. */
282 ret = create_tmp_reg (TREE_TYPE (ptr), "adjusted_this");
283 stmt = gimple_build_assign (ret, ptr);
284 gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
285
286 return ret;
287}
288
289/* Expand thunk NODE to gimple if possible.
290 When FORCE_GIMPLE_THUNK is true, gimple thunk is created and
291 no assembler is produced.
292 When OUTPUT_ASM_THUNK is true, also produce assembler for
293 thunks that are not lowered. */
294bool
295expand_thunk (cgraph_node *node, bool output_asm_thunks,
296 bool force_gimple_thunk)
297{
298 thunk_info *info = thunk_info::get (node);
299 bool this_adjusting = info->this_adjusting;
300 HOST_WIDE_INT fixed_offset = info->fixed_offset;
301 HOST_WIDE_INT virtual_value = info->virtual_value;
302 HOST_WIDE_INT indirect_offset = info->indirect_offset;
303 tree virtual_offset = NULL;
304 tree alias = node->callees->callee->decl;
305 tree thunk_fndecl = node->decl;
306 tree a;
307
308 if (!force_gimple_thunk
309 && this_adjusting
310 && indirect_offset == 0
311 && !DECL_EXTERNAL (alias)
312 && !DECL_STATIC_CHAIN (alias)
313 && targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
314 virtual_value, alias))
315 {
316 tree fn_block;
317 tree restype = TREE_TYPE (TREE_TYPE (thunk_fndecl));
318
319 if (!output_asm_thunks)
320 {
321 node->analyzed = true;
322 return false;
323 }
324
325 if (in_lto_p)
326 node->get_untransformed_body ();
327 a = DECL_ARGUMENTS (thunk_fndecl);
328
329 current_function_decl = thunk_fndecl;
330
331 /* Ensure thunks are emitted in their correct sections. */
332 resolve_unique_section (thunk_fndecl, 0,
333 flag_function_sections);
334
335 DECL_RESULT (thunk_fndecl)
336 = build_decl (DECL_SOURCE_LOCATION (thunk_fndecl),
337 RESULT_DECL, 0, restype);
338 DECL_CONTEXT (DECL_RESULT (thunk_fndecl)) = thunk_fndecl;
339
340 /* The back end expects DECL_INITIAL to contain a BLOCK, so we
341 create one. */
342 fn_block = make_node (BLOCK);
343 BLOCK_VARS (fn_block) = a;
344 DECL_INITIAL (thunk_fndecl) = fn_block;
345 BLOCK_SUPERCONTEXT (fn_block) = thunk_fndecl;
346 allocate_struct_function (thunk_fndecl, false);
347 init_function_start (thunk_fndecl);
348 cfun->is_thunk = 1;
349 insn_locations_init ();
350 set_curr_insn_location (DECL_SOURCE_LOCATION (thunk_fndecl));
351 prologue_location = curr_insn_location ();
352
353 targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl,
354 fixed_offset, virtual_value, alias);
355
356 insn_locations_finalize ();
357 init_insn_lengths ();
358 free_after_compilation (cfun);
359 TREE_ASM_WRITTEN (thunk_fndecl) = 1;
360 node->thunk = false;
361 node->analyzed = false;
362 }
363 else if (stdarg_p (TREE_TYPE (thunk_fndecl)))
364 {
365 error ("generic thunk code fails for method %qD which uses %<...%>",
366 thunk_fndecl);
367 TREE_ASM_WRITTEN (thunk_fndecl) = 1;
368 node->analyzed = true;
369 return false;
370 }
371 else
372 {
373 tree restype;
374 basic_block bb, then_bb, else_bb, return_bb;
375 gimple_stmt_iterator bsi;
376 int nargs = 0;
377 tree arg;
378 int i;
379 tree resdecl;
380 tree restmp = NULL;
381
382 gcall *call;
383 greturn *ret;
384 bool alias_is_noreturn = TREE_THIS_VOLATILE (alias);
385
386 /* We may be called from expand_thunk that releases body except for
387 DECL_ARGUMENTS. In this case force_gimple_thunk is true. */
388 if (in_lto_p && !force_gimple_thunk)
389 node->get_untransformed_body ();
390
391 /* We need to force DECL_IGNORED_P when the thunk is created
392 after early debug was run. */
393 if (force_gimple_thunk)
394 DECL_IGNORED_P (thunk_fndecl) = 1;
395
396 a = DECL_ARGUMENTS (thunk_fndecl);
397
398 current_function_decl = thunk_fndecl;
399
400 /* Ensure thunks are emitted in their correct sections. */
401 resolve_unique_section (thunk_fndecl, 0,
402 flag_function_sections);
403
404 bitmap_obstack_initialize (NULL);
405
406 if (info->virtual_offset_p)
407 virtual_offset = size_int (virtual_value);
408
409 /* Build the return declaration for the function. */
410 restype = TREE_TYPE (TREE_TYPE (thunk_fndecl));
411 if (DECL_RESULT (thunk_fndecl) == NULL_TREE)
412 {
413 resdecl = build_decl (input_location, RESULT_DECL, 0, restype);
414 DECL_ARTIFICIAL (resdecl) = 1;
415 DECL_IGNORED_P (resdecl) = 1;
416 DECL_CONTEXT (resdecl) = thunk_fndecl;
417 DECL_RESULT (thunk_fndecl) = resdecl;
418 }
419 else
420 resdecl = DECL_RESULT (thunk_fndecl);
421
422 profile_count cfg_count = node->count;
423 if (!cfg_count.initialized_p ())
424 cfg_count = profile_count::from_gcov_type
425 (BB_FREQ_MAX).guessed_local ();
426
427 bb = then_bb = else_bb = return_bb
428 = init_lowered_empty_function (thunk_fndecl, true, cfg_count);
429
430 bsi = gsi_start_bb (bb);
431
432 /* Build call to the function being thunked. */
433 if (!VOID_TYPE_P (restype)
434 && (!alias_is_noreturn
435 || TREE_ADDRESSABLE (restype)
436 || TREE_CODE (TYPE_SIZE_UNIT (restype)) != INTEGER_CST))
437 {
438 if (DECL_BY_REFERENCE (resdecl))
439 {
440 restmp = gimple_fold_indirect_ref (resdecl);
441 if (!restmp)
442 restmp = build2 (MEM_REF,
443 TREE_TYPE (TREE_TYPE (resdecl)),
444 resdecl,
445 build_int_cst (TREE_TYPE (resdecl), 0));
446 }
447 else if (!is_gimple_reg_type (restype))
448 {
449 if (aggregate_value_p (resdecl, TREE_TYPE (thunk_fndecl)))
450 {
451 restmp = resdecl;
452
453 if (VAR_P (restmp))
454 {
455 add_local_decl (cfun, restmp);
456 BLOCK_VARS (DECL_INITIAL (current_function_decl))
457 = restmp;
458 }
459 }
460 else
461 restmp = create_tmp_var (restype, "retval");
462 }
463 else
464 restmp = create_tmp_reg (restype, "retval");
465 }
466
467 for (arg = a; arg; arg = DECL_CHAIN (arg))
468 nargs++;
469 auto_vec<tree> vargs (nargs);
470 i = 0;
471 arg = a;
472 if (this_adjusting)
473 {
474 vargs.quick_push (thunk_adjust (&bsi, a, 1, fixed_offset,
475 virtual_offset, indirect_offset));
476 arg = DECL_CHAIN (a);
477 i = 1;
478 }
479
480 if (nargs)
481 for (; i < nargs; i++, arg = DECL_CHAIN (arg))
482 {
483 tree tmp = arg;
484 DECL_NOT_GIMPLE_REG_P (arg) = 0;
485 if (!is_gimple_val (arg))
486 {
487 tmp = create_tmp_reg (TYPE_MAIN_VARIANT
488 (TREE_TYPE (arg)), "arg");
489 gimple *stmt = gimple_build_assign (tmp, arg);
490 gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
491 }
492 vargs.quick_push (tmp);
493 }
494 call = gimple_build_call_vec (build_fold_addr_expr_loc (0, alias), vargs);
495 node->callees->call_stmt = call;
496 gimple_call_set_from_thunk (call, true);
497 if (DECL_STATIC_CHAIN (alias))
498 {
499 tree p = DECL_STRUCT_FUNCTION (alias)->static_chain_decl;
500 tree type = TREE_TYPE (p);
501 tree decl = build_decl (DECL_SOURCE_LOCATION (thunk_fndecl),
502 PARM_DECL, create_tmp_var_name ("CHAIN"),
503 type);
504 DECL_ARTIFICIAL (decl) = 1;
505 DECL_IGNORED_P (decl) = 1;
506 TREE_USED (decl) = 1;
507 DECL_CONTEXT (decl) = thunk_fndecl;
508 DECL_ARG_TYPE (decl) = type;
509 TREE_READONLY (decl) = 1;
510
511 struct function *sf = DECL_STRUCT_FUNCTION (thunk_fndecl);
512 sf->static_chain_decl = decl;
513
514 gimple_call_set_chain (call, decl);
515 }
516
517 /* Return slot optimization is always possible and in fact required to
518 return values with DECL_BY_REFERENCE. */
519 if (aggregate_value_p (resdecl, TREE_TYPE (thunk_fndecl))
520 && (!is_gimple_reg_type (TREE_TYPE (resdecl))
521 || DECL_BY_REFERENCE (resdecl)))
522 gimple_call_set_return_slot_opt (call, true);
523
524 if (restmp)
525 {
526 gimple_call_set_lhs (call, restmp);
527 gcc_assert (useless_type_conversion_p (TREE_TYPE (restmp),
528 TREE_TYPE (TREE_TYPE (alias))));
529 }
530 gsi_insert_after (&bsi, call, GSI_NEW_STMT);
531 if (!alias_is_noreturn)
532 {
533 if (restmp && !this_adjusting
534 && (fixed_offset || virtual_offset))
535 {
536 tree true_label = NULL_TREE;
537
538 if (TREE_CODE (TREE_TYPE (restmp)) == POINTER_TYPE)
539 {
540 gimple *stmt;
541 edge e;
542 /* If the return type is a pointer, we need to
543 protect against NULL. We know there will be an
544 adjustment, because that's why we're emitting a
545 thunk. */
546 then_bb = create_basic_block (NULL, bb);
547 then_bb->count = cfg_count - cfg_count.apply_scale (1, 16);
548 return_bb = create_basic_block (NULL, then_bb);
549 return_bb->count = cfg_count;
550 else_bb = create_basic_block (NULL, else_bb);
551 else_bb->count = cfg_count.apply_scale (1, 16);
552 add_bb_to_loop (then_bb, bb->loop_father);
553 add_bb_to_loop (return_bb, bb->loop_father);
554 add_bb_to_loop (else_bb, bb->loop_father);
555 remove_edge (single_succ_edge (bb));
556 true_label = gimple_block_label (then_bb);
557 stmt = gimple_build_cond (NE_EXPR, restmp,
558 build_zero_cst (TREE_TYPE (restmp)),
559 NULL_TREE, NULL_TREE);
560 gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
561 e = make_edge (bb, then_bb, EDGE_TRUE_VALUE);
562 e->probability = profile_probability::guessed_always ()
563 .apply_scale (1, 16);
564 e = make_edge (bb, else_bb, EDGE_FALSE_VALUE);
565 e->probability = profile_probability::guessed_always ()
566 .apply_scale (1, 16);
567 make_single_succ_edge (return_bb,
568 EXIT_BLOCK_PTR_FOR_FN (cfun), 0);
569 make_single_succ_edge (then_bb, return_bb, EDGE_FALLTHRU);
570 e = make_edge (else_bb, return_bb, EDGE_FALLTHRU);
571 e->probability = profile_probability::always ();
572 bsi = gsi_last_bb (then_bb);
573 }
574
575 restmp = thunk_adjust (&bsi, restmp, /*this_adjusting=*/0,
576 fixed_offset, virtual_offset,
577 indirect_offset);
578 if (true_label)
579 {
580 gimple *stmt;
581 bsi = gsi_last_bb (else_bb);
582 stmt = gimple_build_assign (restmp,
583 build_zero_cst
584 (TREE_TYPE (restmp)));
585 gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
586 bsi = gsi_last_bb (return_bb);
587 }
588 }
589 else
590 {
591 gimple_call_set_tail (call, true);
592 cfun->tail_call_marked = true;
593 }
594
595 /* Build return value. */
596 if (!DECL_BY_REFERENCE (resdecl))
597 ret = gimple_build_return (restmp);
598 else
599 ret = gimple_build_return (resdecl);
600
601 gsi_insert_after (&bsi, ret, GSI_NEW_STMT);
602 }
603 else
604 {
605 gimple_call_set_tail (call, true);
606 cfun->tail_call_marked = true;
607 remove_edge (single_succ_edge (bb));
608 }
609
610 cfun->gimple_df->in_ssa_p = true;
611 update_max_bb_count ();
612 profile_status_for_fn (cfun)
613 = cfg_count.initialized_p () && cfg_count.ipa_p ()
614 ? PROFILE_READ : PROFILE_GUESSED;
615 /* FIXME: C++ FE should stop setting TREE_ASM_WRITTEN on thunks. */
616 TREE_ASM_WRITTEN (thunk_fndecl) = false;
617 delete_unreachable_blocks ();
618 update_ssa (TODO_update_ssa);
619 checking_verify_flow_info ();
620 free_dominance_info (CDI_DOMINATORS);
621
622 /* Since we want to emit the thunk, we explicitly mark its name as
623 referenced. */
624 node->thunk = false;
625 node->lowered = true;
626 bitmap_obstack_release (NULL);
627 }
628 current_function_decl = NULL;
629 set_cfun (NULL);
630 return true;
631}
632
633void
634symtab_thunks_cc_finalize (void)
635{
636 vtable_entry_type = NULL;
637}
638
639#include "gt-symtab-thunks.h"