1 /* Support for thunks in symbol table.
2 Copyright (C) 2003-2020 Free Software Foundation, Inc.
3 Contributed by Jan Hubicka
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
23 #include "coretypes.h"
30 #include "alloc-pool.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"
43 #include "gimple-ssa.h"
44 #include "gimple-fold.h"
46 #include "tree-into-ssa.h"
48 #include "cfgcleanup.h"
49 #include "tree-pass.h"
50 #include "data-streamer.h"
51 #include "langhooks.h"
53 /* Used for vtable lookup in thunk adjusting. */
54 static GTY (()) tree vtable_entry_type
;
58 /* Function summary for thunk_infos. */
59 class GTY((user
)) thunk_infos_t
: public function_summary
<thunk_info
*>
62 thunk_infos_t (symbol_table
*table
, bool ggc
):
63 function_summary
<thunk_info
*> (table
, ggc
) { }
65 /* Hook that is called by summary when a node is duplicated. */
66 virtual void duplicate (cgraph_node
*node
,
72 /* Duplication hook. */
74 thunk_infos_t::duplicate (cgraph_node
*, cgraph_node
*,
75 thunk_info
*src
, thunk_info
*dst
)
80 } /* anon namespace */
82 /* Return thunk_info possibly creating new one. */
84 thunk_info::get_create (cgraph_node
*node
)
86 if (!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 ();
93 return symtab
->m_thunks
->get_create (node
);
96 /* Stream out THIS to OB. */
98 thunk_info::stream_out (lto_simple_output_block
*ob
)
100 streamer_write_uhwi_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
);
109 /* Stream in THIS from IB. */
111 thunk_info::stream_in (class lto_input_block
*ib
)
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
);
118 this_adjusting
= (type
& 2);
119 virtual_offset_p
= (type
& 4);
122 /* Dump THIS to F. */
124 thunk_info::dump (FILE *f
)
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",
134 (int)indirect_offset
,
135 (int)virtual_offset_p
);
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 ();
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. */
155 thunk_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
)
164 && fixed_offset
!= 0)
166 stmt
= gimple_build_assign
167 (ptr
, fold_build_pointer_plus_hwi_loc (input_location
,
170 gsi_insert_after (bsi
, stmt
, GSI_NEW_STMT
);
173 if (!vtable_entry_type
&& (virtual_offset
|| indirect_offset
!= 0))
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
);
180 vtable_entry_type
= build_pointer_type (vfunc_type
);
183 /* If there's a virtual offset, look up that value in the vtable and
184 adjust the pointer again. */
191 vtabletmp
= create_tmp_reg
193 (build_pointer_type (vtable_entry_type
)), "vptr");
195 /* The vptr is always at offset zero in the object. */
196 stmt
= gimple_build_assign (vtabletmp
,
197 build1 (NOP_EXPR
, TREE_TYPE (vtabletmp
),
199 gsi_insert_after (bsi
, stmt
, GSI_NEW_STMT
);
201 /* Form the vtable address. */
202 vtabletmp2
= create_tmp_reg (TREE_TYPE (TREE_TYPE (vtabletmp
)),
204 stmt
= gimple_build_assign (vtabletmp2
,
205 build_simple_mem_ref (vtabletmp
));
206 gsi_insert_after (bsi
, stmt
, GSI_NEW_STMT
);
208 /* Find the entry with the vcall offset. */
209 stmt
= gimple_build_assign (vtabletmp2
,
210 fold_build_pointer_plus_loc (input_location
,
213 gsi_insert_after (bsi
, stmt
, GSI_NEW_STMT
);
215 /* Get the offset itself. */
216 vtabletmp3
= create_tmp_reg (TREE_TYPE (TREE_TYPE (vtabletmp2
)),
218 stmt
= gimple_build_assign (vtabletmp3
,
219 build_simple_mem_ref (vtabletmp2
));
220 gsi_insert_after (bsi
, stmt
, GSI_NEW_STMT
);
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
);
228 /* Likewise for an offset that is stored in the object that contains the
230 if (indirect_offset
!= 0)
232 tree offset_ptr
, offset_tree
;
234 /* Get the address of the offset. */
236 = create_tmp_reg (build_pointer_type
237 (build_pointer_type (vtable_entry_type
)),
239 stmt
= gimple_build_assign (offset_ptr
,
240 build1 (NOP_EXPR
, TREE_TYPE (offset_ptr
),
242 gsi_insert_after (bsi
, stmt
, GSI_NEW_STMT
);
244 stmt
= gimple_build_assign
246 fold_build_pointer_plus_hwi_loc (input_location
, offset_ptr
,
248 gsi_insert_after (bsi
, stmt
, GSI_NEW_STMT
);
250 /* Get the offset itself. */
251 offset_tree
= create_tmp_reg (TREE_TYPE (TREE_TYPE (offset_ptr
)),
253 stmt
= gimple_build_assign (offset_tree
,
254 build_simple_mem_ref (offset_ptr
));
255 gsi_insert_after (bsi
, stmt
, GSI_NEW_STMT
);
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
);
264 && fixed_offset
!= 0)
265 /* Adjust the pointer by the constant. */
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
);
277 ptr
= fold_build_pointer_plus_hwi_loc (input_location
,
278 ptrtmp
, fixed_offset
);
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
);
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. */
295 expand_thunk (cgraph_node
*node
, bool output_asm_thunks
,
296 bool force_gimple_thunk
)
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
;
308 if (!force_gimple_thunk
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
))
317 tree restype
= TREE_TYPE (TREE_TYPE (thunk_fndecl
));
319 if (!output_asm_thunks
)
321 node
->analyzed
= true;
326 node
->get_untransformed_body ();
327 a
= DECL_ARGUMENTS (thunk_fndecl
);
329 current_function_decl
= thunk_fndecl
;
331 /* Ensure thunks are emitted in their correct sections. */
332 resolve_unique_section (thunk_fndecl
, 0,
333 flag_function_sections
);
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
;
340 /* The back end expects DECL_INITIAL to contain a BLOCK, so we
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
);
349 insn_locations_init ();
350 set_curr_insn_location (DECL_SOURCE_LOCATION (thunk_fndecl
));
351 prologue_location
= curr_insn_location ();
353 targetm
.asm_out
.output_mi_thunk (asm_out_file
, thunk_fndecl
,
354 fixed_offset
, virtual_value
, alias
);
356 insn_locations_finalize ();
357 init_insn_lengths ();
358 free_after_compilation (cfun
);
359 TREE_ASM_WRITTEN (thunk_fndecl
) = 1;
361 node
->analyzed
= false;
363 else if (stdarg_p (TREE_TYPE (thunk_fndecl
)))
365 error ("generic thunk code fails for method %qD which uses %<...%>",
367 TREE_ASM_WRITTEN (thunk_fndecl
) = 1;
368 node
->analyzed
= true;
374 basic_block bb
, then_bb
, else_bb
, return_bb
;
375 gimple_stmt_iterator bsi
;
384 bool alias_is_noreturn
= TREE_THIS_VOLATILE (alias
);
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 ();
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;
396 a
= DECL_ARGUMENTS (thunk_fndecl
);
398 current_function_decl
= thunk_fndecl
;
400 /* Ensure thunks are emitted in their correct sections. */
401 resolve_unique_section (thunk_fndecl
, 0,
402 flag_function_sections
);
404 bitmap_obstack_initialize (NULL
);
406 if (info
->virtual_offset_p
)
407 virtual_offset
= size_int (virtual_value
);
409 /* Build the return declaration for the function. */
410 restype
= TREE_TYPE (TREE_TYPE (thunk_fndecl
));
411 if (DECL_RESULT (thunk_fndecl
) == NULL_TREE
)
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
;
420 resdecl
= DECL_RESULT (thunk_fndecl
);
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 ();
427 bb
= then_bb
= else_bb
= return_bb
428 = init_lowered_empty_function (thunk_fndecl
, true, cfg_count
);
430 bsi
= gsi_start_bb (bb
);
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
))
438 if (DECL_BY_REFERENCE (resdecl
))
440 restmp
= gimple_fold_indirect_ref (resdecl
);
442 restmp
= build2 (MEM_REF
,
443 TREE_TYPE (TREE_TYPE (resdecl
)),
445 build_int_cst (TREE_TYPE (resdecl
), 0));
447 else if (!is_gimple_reg_type (restype
))
449 if (aggregate_value_p (resdecl
, TREE_TYPE (thunk_fndecl
)))
455 add_local_decl (cfun
, restmp
);
456 BLOCK_VARS (DECL_INITIAL (current_function_decl
))
461 restmp
= create_tmp_var (restype
, "retval");
464 restmp
= create_tmp_reg (restype
, "retval");
467 for (arg
= a
; arg
; arg
= DECL_CHAIN (arg
))
469 auto_vec
<tree
> vargs (nargs
);
474 vargs
.quick_push (thunk_adjust (&bsi
, a
, 1, fixed_offset
,
475 virtual_offset
, indirect_offset
));
476 arg
= DECL_CHAIN (a
);
481 for (; i
< nargs
; i
++, arg
= DECL_CHAIN (arg
))
484 DECL_NOT_GIMPLE_REG_P (arg
) = 0;
485 if (!is_gimple_val (arg
))
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
);
492 vargs
.quick_push (tmp
);
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
))
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"),
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;
511 struct function
*sf
= DECL_STRUCT_FUNCTION (thunk_fndecl
);
512 sf
->static_chain_decl
= decl
;
514 gimple_call_set_chain (call
, decl
);
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);
526 gimple_call_set_lhs (call
, restmp
);
527 gcc_assert (useless_type_conversion_p (TREE_TYPE (restmp
),
528 TREE_TYPE (TREE_TYPE (alias
))));
530 gsi_insert_after (&bsi
, call
, GSI_NEW_STMT
);
531 if (!alias_is_noreturn
)
533 if (restmp
&& !this_adjusting
534 && (fixed_offset
|| virtual_offset
))
536 tree true_label
= NULL_TREE
;
538 if (TREE_CODE (TREE_TYPE (restmp
)) == POINTER_TYPE
)
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
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
);
575 restmp
= thunk_adjust (&bsi
, restmp
, /*this_adjusting=*/0,
576 fixed_offset
, virtual_offset
,
581 bsi
= gsi_last_bb (else_bb
);
582 stmt
= gimple_build_assign (restmp
,
584 (TREE_TYPE (restmp
)));
585 gsi_insert_after (&bsi
, stmt
, GSI_NEW_STMT
);
586 bsi
= gsi_last_bb (return_bb
);
591 gimple_call_set_tail (call
, true);
592 cfun
->tail_call_marked
= true;
595 /* Build return value. */
596 if (!DECL_BY_REFERENCE (resdecl
))
597 ret
= gimple_build_return (restmp
);
599 ret
= gimple_build_return (resdecl
);
601 gsi_insert_after (&bsi
, ret
, GSI_NEW_STMT
);
605 gimple_call_set_tail (call
, true);
606 cfun
->tail_call_marked
= true;
607 remove_edge (single_succ_edge (bb
));
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
);
622 /* Since we want to emit the thunk, we explicitly mark its name as
625 node
->lowered
= true;
626 bitmap_obstack_release (NULL
);
628 current_function_decl
= NULL
;
634 symtab_thunks_cc_finalize (void)
636 vtable_entry_type
= NULL
;
639 #include "gt-symtab-thunks.h"