summaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorEric Botcazou <ebotcazou@adacore.com>2018-09-28 21:20:53 +0000
committerEric Botcazou <ebotcazou@gcc.gnu.org>2018-09-28 21:20:53 +0000
commit44662f681e8be6b3b7eafcec4afa0ecb8cc587cd (patch)
tree1c0470d4d2c3d3ea333d09879f17a86087934d93 /gcc
parent5c441345a343d0ef90e5ac9ef95039030ecef1ee (diff)
calls.c (expand_call): Try to do a tail call for thunks at -O0 too.
* calls.c (expand_call): Try to do a tail call for thunks at -O0 too. * cgraph.h (struct cgraph_thunk_info): Add indirect_offset. (cgraph_node::create_thunk): Add indirect_offset parameter. (thunk_adjust): Likewise. * cgraph.c (cgraph_node::create_thunk): Add indirect_offset parameter and initialize the corresponding field with it. (cgraph_node::dump): Dump indirect_offset field. * cgraphclones.c (duplicate_thunk_for_node): Deal with indirect_offset. * cgraphunit.c (cgraph_node::analyze): Be prepared for external thunks. (thunk_adjust): Add indirect_offset parameter and deal with it. (cgraph_node::expand_thunk): Deal with the indirect_offset field and pass it to thunk_adjust. Do not call the target hook if it's non-zero or if the thunk is external or local. Fix formatting. Do not chain the RESULT_DECL to BLOCK_VARS. Pass the static chain to the target, if any, in the GIMPLE representation. * ipa-icf.c (sem_function::equals_wpa): Deal with indirect_offset. * lto-cgraph.c (lto_output_node): Write indirect_offset field. (input_node): Read indirect_offset field. * tree-inline.c (expand_call_inline): Pass indirect_offset field in the call to thunk_adjust. * tree-nested.c (struct nesting_info): Add thunk_p field. (create_nesting_tree): Set it. (convert_all_function_calls): Copy static chain from targets to thunks. (finalize_nesting_tree_1): Return early for thunks. (unnest_nesting_tree_1): Do not finalize thunks. (gimplify_all_functions): Do not gimplify thunks. cp/ * method.c (use_thunk): Adjust call to cgraph_node::create_thunk. ada/ * gcc-interface/decl.c (is_cplusplus_method): Do not require C++ convention on Interfaces. * gcc-interface/trans.c (Subprogram_Body_to_gnu): Try to create a bona-fide thunk and hand it over to the middle-end. (get_controlling_type): New function. (use_alias_for_thunk_p): Likewise. (thunk_labelno): New static variable. (make_covariant_thunk): New function. (maybe_make_gnu_thunk): Likewise. * gcc-interface/utils.c (finish_subprog_decl): Set DECL_CONTEXT of the result DECL here instead of... (end_subprog_body): ...here. Co-Authored-By: Pierre-Marie de Rodat <derodat@adacore.com> From-SVN: r264701
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog30
-rw-r--r--gcc/ada/ChangeLog16
-rw-r--r--gcc/ada/gcc-interface/decl.c6
-rw-r--r--gcc/ada/gcc-interface/trans.c242
-rw-r--r--gcc/ada/gcc-interface/utils.c4
-rw-r--r--gcc/calls.c3
-rw-r--r--gcc/cgraph.c7
-rw-r--r--gcc/cgraph.h8
-rw-r--r--gcc/cgraphclones.c7
-rw-r--r--gcc/cgraphunit.c118
-rw-r--r--gcc/cp/ChangeLog5
-rw-r--r--gcc/cp/method.c2
-rw-r--r--gcc/ipa-icf.c2
-rw-r--r--gcc/lto-cgraph.c5
-rw-r--r--gcc/tree-inline.c2
-rw-r--r--gcc/tree-nested.c33
16 files changed, 441 insertions, 49 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 24afef245fc..96baa7d48e2 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,33 @@
+2018-09-28 Eric Botcazou <ebotcazou@adacore.com>
+ Pierre-Marie de Rodat <derodat@adacore.com>
+
+ * calls.c (expand_call): Try to do a tail call for thunks at -O0 too.
+ * cgraph.h (struct cgraph_thunk_info): Add indirect_offset.
+ (cgraph_node::create_thunk): Add indirect_offset parameter.
+ (thunk_adjust): Likewise.
+ * cgraph.c (cgraph_node::create_thunk): Add indirect_offset parameter
+ and initialize the corresponding field with it.
+ (cgraph_node::dump): Dump indirect_offset field.
+ * cgraphclones.c (duplicate_thunk_for_node): Deal with indirect_offset.
+ * cgraphunit.c (cgraph_node::analyze): Be prepared for external thunks.
+ (thunk_adjust): Add indirect_offset parameter and deal with it.
+ (cgraph_node::expand_thunk): Deal with the indirect_offset field and
+ pass it to thunk_adjust. Do not call the target hook if it's non-zero
+ or if the thunk is external or local. Fix formatting. Do not chain
+ the RESULT_DECL to BLOCK_VARS. Pass the static chain to the target,
+ if any, in the GIMPLE representation.
+ * ipa-icf.c (sem_function::equals_wpa): Deal with indirect_offset.
+ * lto-cgraph.c (lto_output_node): Write indirect_offset field.
+ (input_node): Read indirect_offset field.
+ * tree-inline.c (expand_call_inline): Pass indirect_offset field in the
+ call to thunk_adjust.
+ * tree-nested.c (struct nesting_info): Add thunk_p field.
+ (create_nesting_tree): Set it.
+ (convert_all_function_calls): Copy static chain from targets to thunks.
+ (finalize_nesting_tree_1): Return early for thunks.
+ (unnest_nesting_tree_1): Do not finalize thunks.
+ (gimplify_all_functions): Do not gimplify thunks.
+
2018-09-28 David Malcolm <dmalcolm@redhat.com>
* opt-suggestions.c (option_proposer::build_option_suggestions):
diff --git a/gcc/ada/ChangeLog b/gcc/ada/ChangeLog
index ce20db3d0d0..c18c4c8852e 100644
--- a/gcc/ada/ChangeLog
+++ b/gcc/ada/ChangeLog
@@ -1,3 +1,19 @@
+2018-09-28 Eric Botcazou <ebotcazou@adacore.com>
+ Pierre-Marie de Rodat <derodat@adacore.com>
+
+ * gcc-interface/decl.c (is_cplusplus_method): Do not require C++
+ convention on Interfaces.
+ * gcc-interface/trans.c (Subprogram_Body_to_gnu): Try to create a
+ bona-fide thunk and hand it over to the middle-end.
+ (get_controlling_type): New function.
+ (use_alias_for_thunk_p): Likewise.
+ (thunk_labelno): New static variable.
+ (make_covariant_thunk): New function.
+ (maybe_make_gnu_thunk): Likewise.
+ * gcc-interface/utils.c (finish_subprog_decl): Set DECL_CONTEXT of the
+ result DECL here instead of...
+ (end_subprog_body): ...here.
+
2018-09-27 Martin Sebor <msebor@redhat.com>
* gcc-interface/utils.c (make_packable_type): Introduce a temporary
diff --git a/gcc/ada/gcc-interface/decl.c b/gcc/ada/gcc-interface/decl.c
index 8dd62f73d5e..4a1845a33b6 100644
--- a/gcc/ada/gcc-interface/decl.c
+++ b/gcc/ada/gcc-interface/decl.c
@@ -4851,15 +4851,15 @@ is_cplusplus_method (Entity_Id gnat_entity)
if (Convention (gnat_entity) != Convention_CPP)
return false;
- /* And that the type of the first parameter (indirectly) has it too. */
+ /* And that the type of the first parameter (indirectly) has it too, but
+ we make an exception for Interfaces because they need not be imported. */
Entity_Id gnat_first = First_Formal (gnat_entity);
if (No (gnat_first))
return false;
-
Entity_Id gnat_type = Etype (gnat_first);
if (Is_Access_Type (gnat_type))
gnat_type = Directly_Designated_Type (gnat_type);
- if (Convention (gnat_type) != Convention_CPP)
+ if (Convention (gnat_type) != Convention_CPP && !Is_Interface (gnat_type))
return false;
/* This is the main case: a C++ virtual method imported as a primitive
diff --git a/gcc/ada/gcc-interface/trans.c b/gcc/ada/gcc-interface/trans.c
index 12f6a87437f..53513e538f1 100644
--- a/gcc/ada/gcc-interface/trans.c
+++ b/gcc/ada/gcc-interface/trans.c
@@ -247,6 +247,7 @@ static bool set_end_locus_from_node (tree, Node_Id);
static int lvalue_required_p (Node_Id, tree, bool, bool);
static tree build_raise_check (int, enum exception_info_kind);
static tree create_init_temporary (const char *, tree, tree *, Node_Id);
+static bool maybe_make_gnu_thunk (Entity_Id gnat_thunk, tree gnu_thunk);
/* Hooks for debug info back-ends, only supported and used in a restricted set
of configurations. */
@@ -3791,6 +3792,11 @@ Subprogram_Body_to_gnu (Node_Id gnat_node)
if (Was_Expression_Function (gnat_node))
DECL_DISREGARD_INLINE_LIMITS (gnu_subprog_decl) = 1;
+ /* Try to create a bona-fide thunk and hand it over to the middle-end. */
+ if (Is_Thunk (gnat_subprog_id)
+ && maybe_make_gnu_thunk (gnat_subprog_id, gnu_subprog_decl))
+ return;
+
/* Initialize the information structure for the function. */
allocate_struct_function (gnu_subprog_decl, false);
gnu_subprog_language = ggc_cleared_alloc<language_function> ();
@@ -10333,6 +10339,242 @@ get_elaboration_procedure (void)
return gnu_elab_proc_stack->last ();
}
+/* Return the controlling type of a dispatching subprogram. */
+
+static Entity_Id
+get_controlling_type (Entity_Id subprog)
+{
+ /* This is modelled on Expand_Interface_Thunk. */
+ Entity_Id controlling_type = Etype (First_Formal (subprog));
+ if (Is_Access_Type (controlling_type))
+ controlling_type = Directly_Designated_Type (controlling_type);
+ controlling_type = Underlying_Type (controlling_type);
+ if (Is_Concurrent_Type (controlling_type))
+ controlling_type = Corresponding_Record_Type (controlling_type);
+ controlling_type = Base_Type (controlling_type);
+ return controlling_type;
+}
+
+/* Return whether we should use an alias for the TARGET of a thunk
+ in order to make the call generated in the thunk local. */
+
+static bool
+use_alias_for_thunk_p (tree target)
+{
+ /* We cannot generate a local call in this case. */
+ if (DECL_EXTERNAL (target))
+ return false;
+
+ /* The call is already local in this case. */
+ if (TREE_CODE (DECL_CONTEXT (target)) == FUNCTION_DECL)
+ return false;
+
+ return TARGET_USE_LOCAL_THUNK_ALIAS_P (target);
+}
+
+static GTY(()) unsigned long thunk_labelno = 0;
+
+/* Create an alias for TARGET to be used as the target of a thunk. */
+
+static tree
+make_alias_for_thunk (tree target)
+{
+ char buf[64];
+ targetm.asm_out.generate_internal_label (buf, "LTHUNK", thunk_labelno++);
+
+ tree alias = build_decl (DECL_SOURCE_LOCATION (target), TREE_CODE (target),
+ get_identifier (buf), TREE_TYPE (target));
+
+ DECL_LANG_SPECIFIC (alias) = DECL_LANG_SPECIFIC (target);
+ DECL_CONTEXT (alias) = DECL_CONTEXT (target);
+ TREE_READONLY (alias) = TREE_READONLY (target);
+ TREE_THIS_VOLATILE (alias) = TREE_THIS_VOLATILE (target);
+ DECL_ARTIFICIAL (alias) = 1;
+ DECL_INITIAL (alias) = error_mark_node;
+ DECL_ARGUMENTS (alias) = copy_list (DECL_ARGUMENTS (target));
+ TREE_ADDRESSABLE (alias) = 1;
+ SET_DECL_ASSEMBLER_NAME (alias, DECL_NAME (alias));
+
+ cgraph_node *n = cgraph_node::create_same_body_alias (alias, target);
+ gcc_assert (n);
+
+ return alias;
+}
+
+/* Create the covariant part of the {GNAT,GNU}_THUNK. */
+
+static tree
+make_covariant_thunk (Entity_Id gnat_thunk, tree gnu_thunk)
+{
+ tree gnu_name = create_concat_name (gnat_thunk, "CV");
+ tree gnu_cv_thunk
+ = build_decl (DECL_SOURCE_LOCATION (gnu_thunk), TREE_CODE (gnu_thunk),
+ gnu_name, TREE_TYPE (gnu_thunk));
+
+ DECL_ARGUMENTS (gnu_cv_thunk) = copy_list (DECL_ARGUMENTS (gnu_thunk));
+ DECL_RESULT (gnu_cv_thunk) = copy_node (DECL_RESULT (gnu_thunk));
+ DECL_CONTEXT (DECL_RESULT (gnu_cv_thunk)) = gnu_cv_thunk;
+
+ DECL_LANG_SPECIFIC (gnu_cv_thunk) = DECL_LANG_SPECIFIC (gnu_thunk);
+ DECL_CONTEXT (gnu_cv_thunk) = DECL_CONTEXT (gnu_thunk);
+ TREE_READONLY (gnu_cv_thunk) = TREE_READONLY (gnu_thunk);
+ TREE_THIS_VOLATILE (gnu_cv_thunk) = TREE_THIS_VOLATILE (gnu_thunk);
+ TREE_PUBLIC (gnu_cv_thunk) = TREE_PUBLIC (gnu_thunk);
+ DECL_ARTIFICIAL (gnu_cv_thunk) = 1;
+
+ return gnu_cv_thunk;
+}
+
+/* Try to create a GNU thunk for {GNAT,GNU}_THUNK and return true on success.
+
+ GNU thunks are more efficient than GNAT thunks because they don't call into
+ the runtime to retrieve the offset used in the displacement operation, but
+ they are tailored to C++ and thus too limited to support the full range of
+ thunks generated in Ada. Here's the complete list of limitations:
+
+ 1. Multi-controlling thunks, i.e thunks with more than one controlling
+ parameter, are simply not supported.
+
+ 2. Covariant thunks, i.e. thunks for which the result is also controlling,
+ are split into a pair of (this, covariant-only) thunks.
+
+ 3. Variable-offset thunks, i.e. thunks for which the offset depends on the
+ object and not only on its type, are supported as 2nd class citizens.
+
+ 4. External thunks, i.e. thunks for which the target is not declared in
+ the same unit as the thunk, are supported as 2nd class citizens.
+
+ 5. Local thunks, i.e. thunks generated for a local type, are supported as
+ 2nd class citizens. */
+
+static bool
+maybe_make_gnu_thunk (Entity_Id gnat_thunk, tree gnu_thunk)
+{
+ const Entity_Id gnat_target = Thunk_Entity (gnat_thunk);
+
+ /* Check that the first formal of the target is the only controlling one. */
+ Entity_Id gnat_formal = First_Formal (gnat_target);
+ if (!Is_Controlling_Formal (gnat_formal))
+ return false;
+ for (gnat_formal = Next_Formal (gnat_formal);
+ Present (gnat_formal);
+ gnat_formal = Next_Formal (gnat_formal))
+ if (Is_Controlling_Formal (gnat_formal))
+ return false;
+
+ /* Look for the types that control the target and the thunk. */
+ const Entity_Id gnat_controlling_type = get_controlling_type (gnat_target);
+ const Entity_Id gnat_interface_type = get_controlling_type (gnat_thunk);
+
+ /* Now compute whether the former covers the latter. */
+ const Entity_Id gnat_interface_tag
+ = Is_Interface (gnat_interface_type)
+ ? Find_Interface_Tag (gnat_controlling_type, gnat_interface_type)
+ : Empty;
+ tree gnu_interface_tag
+ = Present (gnat_interface_tag)
+ ? gnat_to_gnu_field_decl (gnat_interface_tag)
+ : NULL_TREE;
+ tree gnu_interface_offset
+ = gnu_interface_tag ? byte_position (gnu_interface_tag) : NULL_TREE;
+
+ /* There are three ways to retrieve the offset between the interface view
+ and the base object. Either the controlling type covers the interface
+ type and the offset of the corresponding tag is fixed, in which case it
+ can be statically encoded in the thunk (see FIXED_OFFSET below). Or the
+ controlling type doesn't cover the interface type but is of fixed size,
+ in which case the offset is stored in the dispatch table, two pointers
+ above the dispatch table address (see VIRTUAL_VALUE below). Otherwise,
+ the offset is variable and is stored right after the tag in every object
+ (see INDIRECT_OFFSET below). See also a-tags.ads for more details. */
+ HOST_WIDE_INT fixed_offset, virtual_value, indirect_offset;
+ tree virtual_offset;
+
+ if (gnu_interface_offset && TREE_CODE (gnu_interface_offset) == INTEGER_CST)
+ {
+ fixed_offset = - tree_to_shwi (gnu_interface_offset);
+ virtual_value = 0;
+ virtual_offset = NULL_TREE;
+ indirect_offset = 0;
+ }
+ else if (!gnu_interface_offset
+ && !Is_Variable_Size_Record (gnat_controlling_type))
+ {
+ fixed_offset = 0;
+ virtual_value = - 2 * (HOST_WIDE_INT) (POINTER_SIZE / BITS_PER_UNIT);
+ virtual_offset = build_int_cst (integer_type_node, virtual_value);
+ indirect_offset = 0;
+ }
+ else
+ {
+ /* Covariant thunks with variable offset are not supported. */
+ if (Has_Controlling_Result (gnat_target))
+ return false;
+
+ fixed_offset = 0;
+ virtual_value = 0;
+ virtual_offset = NULL_TREE;
+ indirect_offset = (HOST_WIDE_INT) (POINTER_SIZE / BITS_PER_UNIT);
+ }
+
+ tree gnu_target = gnat_to_gnu_entity (gnat_target, NULL_TREE, false);
+
+ /* Thunk and target must have the same nesting level, if any. */
+ gcc_assert (DECL_CONTEXT (gnu_thunk) == DECL_CONTEXT (gnu_target));
+
+ /* If the target returns by invisible reference and is external, apply the
+ same transformation as Subprogram_Body_to_gnu here. */
+ if (TREE_ADDRESSABLE (TREE_TYPE (gnu_target))
+ && DECL_EXTERNAL (gnu_target)
+ && !POINTER_TYPE_P (TREE_TYPE (DECL_RESULT (gnu_target))))
+ {
+ TREE_TYPE (DECL_RESULT (gnu_target))
+ = build_reference_type (TREE_TYPE (DECL_RESULT (gnu_target)));
+ relayout_decl (DECL_RESULT (gnu_target));
+ }
+
+ /* The thunk expander requires the return types of thunk and target to be
+ compatible, which is not fully the case with the CICO mechanism. */
+ if (TYPE_CI_CO_LIST (TREE_TYPE (gnu_thunk)))
+ {
+ tree gnu_target_type = TREE_TYPE (gnu_target);
+ gcc_assert (TYPE_CI_CO_LIST (gnu_target_type));
+ TYPE_CANONICAL (TREE_TYPE (TREE_TYPE (gnu_thunk)))
+ = TYPE_CANONICAL (TREE_TYPE (gnu_target_type));
+ }
+
+ cgraph_node *target_node = cgraph_node::get_create (gnu_target);
+
+ /* If the return type of the target is a controlling type, then we need
+ both an usual this thunk and a covariant thunk in this order:
+
+ this thunk --> covariant thunk --> target
+
+ For covariant thunks, we can only handle a fixed offset. */
+ if (Has_Controlling_Result (gnat_target))
+ {
+ gcc_assert (fixed_offset < 0);
+ tree gnu_cv_thunk = make_covariant_thunk (gnat_thunk, gnu_thunk);
+ target_node->create_thunk (gnu_cv_thunk, gnu_target, false,
+ - fixed_offset, 0, 0,
+ NULL_TREE, gnu_target);
+
+ gnu_target = gnu_cv_thunk;
+ }
+
+ /* We may also need to create an alias for the target in order to make
+ the call local, depending on the linkage of the target. */
+ tree gnu_alias = use_alias_for_thunk_p (gnu_target)
+ ? make_alias_for_thunk (gnu_target)
+ : gnu_target;
+
+ target_node->create_thunk (gnu_thunk, gnu_target, true,
+ fixed_offset, virtual_value, indirect_offset,
+ virtual_offset, gnu_alias);
+
+ return true;
+}
+
/* Initialize the table that maps GNAT codes to GCC codes for simple
binary and unary operations. */
diff --git a/gcc/ada/gcc-interface/utils.c b/gcc/ada/gcc-interface/utils.c
index 9530165a0c1..1ce2f725927 100644
--- a/gcc/ada/gcc-interface/utils.c
+++ b/gcc/ada/gcc-interface/utils.c
@@ -3294,6 +3294,7 @@ finish_subprog_decl (tree decl, tree asm_name, tree type)
DECL_ARTIFICIAL (result_decl) = 1;
DECL_IGNORED_P (result_decl) = 1;
+ DECL_CONTEXT (result_decl) = decl;
DECL_BY_REFERENCE (result_decl) = TREE_ADDRESSABLE (type);
DECL_RESULT (decl) = result_decl;
@@ -3369,9 +3370,6 @@ end_subprog_body (tree body)
DECL_INITIAL (fndecl) = current_binding_level->block;
gnat_poplevel ();
- /* Mark the RESULT_DECL as being in this subprogram. */
- DECL_CONTEXT (DECL_RESULT (fndecl)) = fndecl;
-
/* The body should be a BIND_EXPR whose BLOCK is the top-level one. */
if (TREE_CODE (body) == BIND_EXPR)
{
diff --git a/gcc/calls.c b/gcc/calls.c
index e9660b62424..80af5c3b3b3 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -3610,9 +3610,8 @@ expand_call (tree exp, rtx target, int ignore)
pushed these optimizations into -O2. Don't try if we're already
expanding a call, as that means we're an argument. Don't try if
there's cleanups, as we know there's code to follow the call. */
-
if (currently_expanding_call++ != 0
- || !flag_optimize_sibling_calls
+ || (!flag_optimize_sibling_calls && !CALL_FROM_THUNK_P (exp))
|| args_size.var
|| dbg_cnt (tail_call) == false)
try_tail_call = 0;
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 148f29ea749..8a03f3d6828 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -617,6 +617,7 @@ cgraph_node *
cgraph_node::create_thunk (tree alias, tree, bool this_adjusting,
HOST_WIDE_INT fixed_offset,
HOST_WIDE_INT virtual_value,
+ HOST_WIDE_INT indirect_offset,
tree virtual_offset,
tree real_alias)
{
@@ -635,6 +636,7 @@ cgraph_node::create_thunk (tree alias, tree, bool this_adjusting,
node->thunk.fixed_offset = fixed_offset;
node->thunk.virtual_value = virtual_value;
+ node->thunk.indirect_offset = indirect_offset;
node->thunk.alias = real_alias;
node->thunk.this_adjusting = this_adjusting;
node->thunk.virtual_offset_p = virtual_offset != NULL;
@@ -2099,10 +2101,11 @@ cgraph_node::dump (FILE *f)
fprintf (f, " of %s (asm: %s)",
lang_hooks.decl_printable_name (thunk.alias, 2),
IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk.alias)));
- fprintf (f, " fixed offset %i virtual value %i has "
- "virtual offset %i)\n",
+ fprintf (f, " fixed offset %i virtual value %i indirect_offset %i "
+ "has virtual offset %i\n",
(int)thunk.fixed_offset,
(int)thunk.virtual_value,
+ (int)thunk.indirect_offset,
(int)thunk.virtual_offset_p);
}
if (alias && thunk.alias
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 2b00f0165fa..71c54537b93 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -666,6 +666,10 @@ struct GTY(()) cgraph_thunk_info {
VIRTUAL_OFFSET_P is true. */
HOST_WIDE_INT virtual_value;
+ /* Offset from "this" to get the offset to adjust "this". Zero means: this
+ offset is to be ignored. */
+ HOST_WIDE_INT indirect_offset;
+
/* Thunk target, i.e. the method that this thunk wraps. Depending on the
TARGET_USE_LOCAL_THUNK_ALIAS_P macro, this may have to be a new alias. */
tree alias;
@@ -1033,6 +1037,7 @@ public:
cgraph_node * create_thunk (tree alias, tree, bool this_adjusting,
HOST_WIDE_INT fixed_offset,
HOST_WIDE_INT virtual_value,
+ HOST_WIDE_INT indirect_offset,
tree virtual_offset,
tree real_alias);
@@ -2373,7 +2378,8 @@ void cgraphunit_c_finalize (void);
IN_SSA is true if the gimple is in SSA. */
basic_block init_lowered_empty_function (tree, bool, profile_count);
-tree thunk_adjust (gimple_stmt_iterator *, tree, bool, HOST_WIDE_INT, tree);
+tree thunk_adjust (gimple_stmt_iterator *, tree, bool, HOST_WIDE_INT, tree,
+ HOST_WIDE_INT);
/* In cgraphclones.c */
tree clone_function_name_1 (const char *, const char *);
diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
index 0c0a94b04a3..2af45bd4fec 100644
--- a/gcc/cgraphclones.c
+++ b/gcc/cgraphclones.c
@@ -274,10 +274,11 @@ duplicate_thunk_for_node (cgraph_node *thunk, cgraph_node *node)
cgraph_edge *cs;
for (cs = node->callers; cs; cs = cs->next_caller)
if (cs->caller->thunk.thunk_p
- && cs->caller->thunk.this_adjusting == thunk->thunk.this_adjusting
&& cs->caller->thunk.fixed_offset == thunk->thunk.fixed_offset
- && cs->caller->thunk.virtual_offset_p == thunk->thunk.virtual_offset_p
- && cs->caller->thunk.virtual_value == thunk->thunk.virtual_value)
+ && cs->caller->thunk.virtual_value == thunk->thunk.virtual_value
+ && cs->caller->thunk.indirect_offset == thunk->thunk.indirect_offset
+ && cs->caller->thunk.this_adjusting == thunk->thunk.this_adjusting
+ && cs->caller->thunk.virtual_offset_p == thunk->thunk.virtual_offset_p)
return cs->caller;
tree new_decl;
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index ec490d75bd1..c0baaeaefa4 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -623,20 +623,18 @@ cgraph_node::analyze (void)
callees->can_throw_external = !TREE_NOTHROW (t->decl);
/* Target code in expand_thunk may need the thunk's target
to be analyzed, so recurse here. */
- if (!t->analyzed)
+ if (!t->analyzed && t->definition)
t->analyze ();
if (t->alias)
{
t = t->get_alias_target ();
- if (!t->analyzed)
+ if (!t->analyzed && t->definition)
t->analyze ();
}
- if (!expand_thunk (false, false))
- {
- thunk.alias = NULL;
- return;
- }
+ bool ret = expand_thunk (false, false);
thunk.alias = NULL;
+ if (!ret)
+ return;
}
if (alias)
resolve_alias (cgraph_node::get (alias_target), transparent_alias);
@@ -1609,15 +1607,16 @@ init_lowered_empty_function (tree decl, bool in_ssa, profile_count count)
return bb;
}
-/* Adjust PTR by the constant FIXED_OFFSET, and by the vtable
- offset indicated by VIRTUAL_OFFSET, if that is
- non-null. THIS_ADJUSTING is nonzero for a this adjusting thunk and
- zero for a result adjusting thunk. */
+/* Adjust PTR by the constant FIXED_OFFSET, by the vtable offset indicated by
+ VIRTUAL_OFFSET, and by the indirect offset indicated by INDIRECT_OFFSET, if
+ it is non-null. THIS_ADJUSTING is nonzero for a this adjusting thunk and zero
+ for a result adjusting thunk. */
tree
thunk_adjust (gimple_stmt_iterator * bsi,
tree ptr, bool this_adjusting,
- HOST_WIDE_INT fixed_offset, tree virtual_offset)
+ HOST_WIDE_INT fixed_offset, tree virtual_offset,
+ HOST_WIDE_INT indirect_offset)
{
gassign *stmt;
tree ret;
@@ -1632,6 +1631,16 @@ thunk_adjust (gimple_stmt_iterator * bsi,
gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
}
+ if (!vtable_entry_type && (virtual_offset || indirect_offset != 0))
+ {
+ tree vfunc_type = make_node (FUNCTION_TYPE);
+ TREE_TYPE (vfunc_type) = integer_type_node;
+ TYPE_ARG_TYPES (vfunc_type) = NULL_TREE;
+ layout_type (vfunc_type);
+
+ vtable_entry_type = build_pointer_type (vfunc_type);
+ }
+
/* If there's a virtual offset, look up that value in the vtable and
adjust the pointer again. */
if (virtual_offset)
@@ -1640,16 +1649,6 @@ thunk_adjust (gimple_stmt_iterator * bsi,
tree vtabletmp2;
tree vtabletmp3;
- if (!vtable_entry_type)
- {
- tree vfunc_type = make_node (FUNCTION_TYPE);
- TREE_TYPE (vfunc_type) = integer_type_node;
- TYPE_ARG_TYPES (vfunc_type) = NULL_TREE;
- layout_type (vfunc_type);
-
- vtable_entry_type = build_pointer_type (vfunc_type);
- }
-
vtabletmp =
create_tmp_reg (build_pointer_type
(build_pointer_type (vtable_entry_type)), "vptr");
@@ -1687,6 +1686,41 @@ thunk_adjust (gimple_stmt_iterator * bsi,
GSI_CONTINUE_LINKING);
}
+ /* Likewise for an offset that is stored in the object that contains the
+ vtable. */
+ if (indirect_offset != 0)
+ {
+ tree offset_ptr, offset_tree;
+
+ /* Get the address of the offset. */
+ offset_ptr
+ = create_tmp_reg (build_pointer_type
+ (build_pointer_type (vtable_entry_type)),
+ "offset_ptr");
+ stmt = gimple_build_assign (offset_ptr,
+ build1 (NOP_EXPR, TREE_TYPE (offset_ptr),
+ ptr));
+ gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+
+ stmt = gimple_build_assign
+ (offset_ptr,
+ fold_build_pointer_plus_hwi_loc (input_location, offset_ptr,
+ indirect_offset));
+ gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+
+ /* Get the offset itself. */
+ offset_tree = create_tmp_reg (TREE_TYPE (TREE_TYPE (offset_ptr)),
+ "offset");
+ stmt = gimple_build_assign (offset_tree,
+ build_simple_mem_ref (offset_ptr));
+ gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+
+ /* Adjust the `this' pointer. */
+ ptr = fold_build_pointer_plus_loc (input_location, ptr, offset_tree);
+ ptr = force_gimple_operand_gsi (bsi, ptr, true, NULL_TREE, false,
+ GSI_CONTINUE_LINKING);
+ }
+
if (!this_adjusting
&& fixed_offset != 0)
/* Adjust the pointer by the constant. */
@@ -1725,6 +1759,7 @@ cgraph_node::expand_thunk (bool output_asm_thunks, bool force_gimple_thunk)
bool this_adjusting = thunk.this_adjusting;
HOST_WIDE_INT fixed_offset = thunk.fixed_offset;
HOST_WIDE_INT virtual_value = thunk.virtual_value;
+ HOST_WIDE_INT indirect_offset = thunk.indirect_offset;
tree virtual_offset = NULL;
tree alias = callees->callee->decl;
tree thunk_fndecl = decl;
@@ -1735,7 +1770,11 @@ cgraph_node::expand_thunk (bool output_asm_thunks, bool force_gimple_thunk)
if (thunk.add_pointer_bounds_args)
return false;
- if (!force_gimple_thunk && this_adjusting
+ if (!force_gimple_thunk
+ && this_adjusting
+ && indirect_offset == 0
+ && !DECL_EXTERNAL (alias)
+ && !DECL_STATIC_CHAIN (alias)
&& targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
virtual_value, alias))
{
@@ -1838,8 +1877,8 @@ cgraph_node::expand_thunk (bool output_asm_thunks, bool force_gimple_thunk)
resdecl = build_decl (input_location, RESULT_DECL, 0, restype);
DECL_ARTIFICIAL (resdecl) = 1;
DECL_IGNORED_P (resdecl) = 1;
+ DECL_CONTEXT (resdecl) = thunk_fndecl;
DECL_RESULT (thunk_fndecl) = resdecl;
- DECL_CONTEXT (DECL_RESULT (thunk_fndecl)) = thunk_fndecl;
}
else
resdecl = DECL_RESULT (thunk_fndecl);
@@ -1876,8 +1915,11 @@ cgraph_node::expand_thunk (bool output_asm_thunks, bool force_gimple_thunk)
restmp = resdecl;
if (VAR_P (restmp))
- add_local_decl (cfun, restmp);
- BLOCK_VARS (DECL_INITIAL (current_function_decl)) = restmp;
+ {
+ add_local_decl (cfun, restmp);
+ BLOCK_VARS (DECL_INITIAL (current_function_decl))
+ = restmp;
+ }
}
else
restmp = create_tmp_var (restype, "retval");
@@ -1894,7 +1936,7 @@ cgraph_node::expand_thunk (bool output_asm_thunks, bool force_gimple_thunk)
if (this_adjusting)
{
vargs.quick_push (thunk_adjust (&bsi, a, 1, fixed_offset,
- virtual_offset));
+ virtual_offset, indirect_offset));
arg = DECL_CHAIN (a);
i = 1;
}
@@ -1919,6 +1961,25 @@ cgraph_node::expand_thunk (bool output_asm_thunks, bool force_gimple_thunk)
call = gimple_build_call_vec (build_fold_addr_expr_loc (0, alias), vargs);
callees->call_stmt = call;
gimple_call_set_from_thunk (call, true);
+ if (DECL_STATIC_CHAIN (alias))
+ {
+ tree p = DECL_STRUCT_FUNCTION (alias)->static_chain_decl;
+ tree type = TREE_TYPE (p);
+ tree decl = build_decl (DECL_SOURCE_LOCATION (thunk_fndecl),
+ PARM_DECL, create_tmp_var_name ("CHAIN"),
+ type);
+ DECL_ARTIFICIAL (decl) = 1;
+ DECL_IGNORED_P (decl) = 1;
+ TREE_USED (decl) = 1;
+ DECL_CONTEXT (decl) = thunk_fndecl;
+ DECL_ARG_TYPE (decl) = type;
+ TREE_READONLY (decl) = 1;
+
+ struct function *sf = DECL_STRUCT_FUNCTION (thunk_fndecl);
+ sf->static_chain_decl = decl;
+
+ gimple_call_set_chain (call, decl);
+ }
/* Return slot optimization is always possible and in fact requred to
return values with DECL_BY_REFERENCE. */
@@ -1979,7 +2040,8 @@ cgraph_node::expand_thunk (bool output_asm_thunks, bool force_gimple_thunk)
}
restmp = thunk_adjust (&bsi, restmp, /*this_adjusting=*/0,
- fixed_offset, virtual_offset);
+ fixed_offset, virtual_offset,
+ indirect_offset);
if (true_label)
{
gimple *stmt;
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index f5d602de026..e10ada2d7b6 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,8 @@
+2018-09-28 Eric Botcazou <ebotcazou@adacore.com>
+ Pierre-Marie de Rodat <derodat@adacore.com>
+
+ * method.c (use_thunk): Adjust call to cgraph_node::create_thunk.
+
2018-09-28 Richard Biener <rguenther@suse.de>
* error.c (cp_print_error_function): Simplify by eliding
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index 5379b4d3d09..2de2d105224 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -375,7 +375,7 @@ use_thunk (tree thunk_fndecl, bool emit_p)
gcc_checking_assert (funcn);
thunk_node = funcn->create_thunk (thunk_fndecl, function,
this_adjusting, fixed_offset, virtual_value,
- virtual_offset, alias);
+ 0, virtual_offset, alias);
if (DECL_ONE_ONLY (function))
thunk_node->add_to_same_comdat_group (funcn);
diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
index 8a6a7a3f32f..3c54f8d4b6d 100644
--- a/gcc/ipa-icf.c
+++ b/gcc/ipa-icf.c
@@ -593,6 +593,8 @@ sem_function::equals_wpa (sem_item *item,
return return_false_with_msg ("thunk fixed_offset mismatch");
if (cnode->thunk.virtual_value != cnode2->thunk.virtual_value)
return return_false_with_msg ("thunk virtual_value mismatch");
+ if (cnode->thunk.indirect_offset != cnode2->thunk.indirect_offset)
+ return return_false_with_msg ("thunk indirect_offset mismatch");
if (cnode->thunk.this_adjusting != cnode2->thunk.this_adjusting)
return return_false_with_msg ("thunk this_adjusting mismatch");
if (cnode->thunk.virtual_offset_p != cnode2->thunk.virtual_offset_p)
diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
index 6d9eea13f22..99998cc3c75 100644
--- a/gcc/lto-cgraph.c
+++ b/gcc/lto-cgraph.c
@@ -556,6 +556,7 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node,
+ (node->thunk.add_pointer_bounds_args != 0) * 8);
streamer_write_uhwi_stream (ob->main_stream, node->thunk.fixed_offset);
streamer_write_uhwi_stream (ob->main_stream, node->thunk.virtual_value);
+ streamer_write_uhwi_stream (ob->main_stream, node->thunk.indirect_offset);
}
streamer_write_hwi_stream (ob->main_stream, node->profile_id);
if (DECL_STATIC_CONSTRUCTOR (node->decl))
@@ -1271,10 +1272,12 @@ input_node (struct lto_file_decl_data *file_data,
int type = streamer_read_uhwi (ib);
HOST_WIDE_INT fixed_offset = streamer_read_uhwi (ib);
HOST_WIDE_INT virtual_value = streamer_read_uhwi (ib);
+ HOST_WIDE_INT indirect_offset = streamer_read_uhwi (ib);
node->thunk.fixed_offset = fixed_offset;
- node->thunk.this_adjusting = (type & 2);
node->thunk.virtual_value = virtual_value;
+ node->thunk.indirect_offset = indirect_offset;
+ node->thunk.this_adjusting = (type & 2);
node->thunk.virtual_offset_p = (type & 4);
node->thunk.add_pointer_bounds_args = (type & 8);
}
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 42c33d23b9b..2eac7b7cc4a 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -4473,7 +4473,7 @@ expand_call_inline (basic_block bb, gimple *stmt, copy_body_data *id)
GSI_NEW_STMT);
gcc_assert (id->src_node->thunk.this_adjusting);
op = thunk_adjust (&iter, op, 1, id->src_node->thunk.fixed_offset,
- virtual_offset);
+ virtual_offset, id->src_node->thunk.indirect_offset);
gimple_call_set_arg (stmt, 0, op);
gimple_call_set_fndecl (stmt, edge->callee->decl);
diff --git a/gcc/tree-nested.c b/gcc/tree-nested.c
index 4c8eda94f14..4579b4c5839 100644
--- a/gcc/tree-nested.c
+++ b/gcc/tree-nested.c
@@ -104,6 +104,7 @@ struct nesting_info
tree chain_decl;
tree nl_goto_field;
+ bool thunk_p;
bool any_parm_remapped;
bool any_tramp_created;
bool any_descr_created;
@@ -834,6 +835,7 @@ create_nesting_tree (struct cgraph_node *cgn)
info->mem_refs = new hash_set<tree *>;
info->suppress_expansion = BITMAP_ALLOC (&nesting_info_bitmap_obstack);
info->context = cgn->decl;
+ info->thunk_p = cgn->thunk.thunk_p;
for (cgn = cgn->nested; cgn ; cgn = cgn->next_nested)
{
@@ -2786,6 +2788,8 @@ convert_all_function_calls (struct nesting_info *root)
within the debugger. */
FOR_EACH_NEST_INFO (n, root)
{
+ if (n->thunk_p)
+ continue;
tree decl = n->context;
if (!optimize)
{
@@ -2806,6 +2810,14 @@ convert_all_function_calls (struct nesting_info *root)
chain_count += DECL_STATIC_CHAIN (decl);
}
+ FOR_EACH_NEST_INFO (n, root)
+ if (n->thunk_p)
+ {
+ tree decl = n->context;
+ tree alias = cgraph_node::get (decl)->thunk.alias;
+ DECL_STATIC_CHAIN (decl) = DECL_STATIC_CHAIN (alias);
+ }
+
/* Walk the functions and perform transformations. Note that these
transformations can induce new uses of the static chain, which in turn
require re-examining all users of the decl. */
@@ -2825,12 +2837,22 @@ convert_all_function_calls (struct nesting_info *root)
FOR_EACH_NEST_INFO (n, root)
{
+ if (n->thunk_p)
+ continue;
tree decl = n->context;
walk_function (convert_tramp_reference_stmt,
convert_tramp_reference_op, n);
walk_function (convert_gimple_call, NULL, n);
chain_count += DECL_STATIC_CHAIN (decl);
}
+
+ FOR_EACH_NEST_INFO (n, root)
+ if (n->thunk_p)
+ {
+ tree decl = n->context;
+ tree alias = cgraph_node::get (decl)->thunk.alias;
+ DECL_STATIC_CHAIN (decl) = DECL_STATIC_CHAIN (alias);
+ }
}
while (chain_count != old_chain_count);
@@ -3055,12 +3077,13 @@ build_init_call_stmt (struct nesting_info *info, tree decl, tree field,
static void
finalize_nesting_tree_1 (struct nesting_info *root)
{
- gimple_seq stmt_list;
+ gimple_seq stmt_list = NULL;
gimple *stmt;
tree context = root->context;
struct function *sf;
- stmt_list = NULL;
+ if (root->thunk_p)
+ return;
/* If we created a non-local frame type or decl, we need to lay them
out at this time. */
@@ -3340,7 +3363,8 @@ unnest_nesting_tree_1 (struct nesting_info *root)
if (node->origin)
{
node->unnest ();
- cgraph_node::finalize_function (root->context, true);
+ if (!root->thunk_p)
+ cgraph_node::finalize_function (root->context, true);
}
}
@@ -3380,7 +3404,8 @@ gimplify_all_functions (struct cgraph_node *root)
if (!gimple_body (root->decl))
gimplify_function_tree (root->decl);
for (iter = root->nested; iter; iter = iter->next_nested)
- gimplify_all_functions (iter);
+ if (!iter->thunk.thunk_p)
+ gimplify_all_functions (iter);
}
/* Main entry point for this pass. Process FNDECL and all of its nested