calls.c (expand_call): Try to do a tail call for thunks at -O0 too.
authorEric Botcazou <ebotcazou@adacore.com>
Fri, 28 Sep 2018 21:20:53 +0000 (21:20 +0000)
committerEric Botcazou <ebotcazou@gcc.gnu.org>
Fri, 28 Sep 2018 21:20:53 +0000 (21:20 +0000)
* 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

16 files changed:
gcc/ChangeLog
gcc/ada/ChangeLog
gcc/ada/gcc-interface/decl.c
gcc/ada/gcc-interface/trans.c
gcc/ada/gcc-interface/utils.c
gcc/calls.c
gcc/cgraph.c
gcc/cgraph.h
gcc/cgraphclones.c
gcc/cgraphunit.c
gcc/cp/ChangeLog
gcc/cp/method.c
gcc/ipa-icf.c
gcc/lto-cgraph.c
gcc/tree-inline.c
gcc/tree-nested.c

index 24afef245fca2935de6654860ea8649cef5b78d3..96baa7d48e2f8c154f0b9f8830759ff0afc5fb42 100644 (file)
@@ -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):
index ce20db3d0d0dcf35cb2de6e635a8378bc18210a3..c18c4c8852eb5a5c8d9940d0ea2728a0eb2c81f7 100644 (file)
@@ -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
index 8dd62f73d5e18cbaec5ab5a5bd0b21fb250383e2..4a1845a33b6d48730b90a4b658c8d311334b03be 100644 (file)
@@ -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
index 12f6a87437fb0036ef878f4b6e48d939daa200cb..53513e538f14b2498bd96fb17916a797381d075d 100644 (file)
@@ -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.  */
 
index 9530165a0c1dbab74905558d39edacdc483656e5..1ce2f7259270741466678d8aaea195fa557e124f 100644 (file)
@@ -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)
     {
index e9660b6242463c5d90772e1a321d09becfefc87c..80af5c3b3b3309637da270b571721e6d567b5c51 100644 (file)
@@ -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;
index 148f29ea749daef482a6b094a0510beaded8d49e..8a03f3d6828d088e8e5649e0eac2fcf237a3b59b 100644 (file)
@@ -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
index 2b00f0165faeb706374a945dd8c99561f1db34ff..71c54537b932d4aef4f226303e90a9edeb700a55 100644 (file)
@@ -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 *);
index 0c0a94b04a3f0ede4c55a2dfd65fa6da35378ee6..2af45bd4fecfa5ee5e59faa845d6b1a7bfb10e59 100644 (file)
@@ -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;
index ec490d75bd1162cf691708c366ab0e86f4eb5a67..c0baaeaefa4ee2a5699e3e402c6d74ff5118e51f 100644 (file)
@@ -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;
index f5d602de026dd0d78bc047aef8d37bf604b6be3f..e10ada2d7b67eaa0d4bb59c41a3cd137e3db46a6 100644 (file)
@@ -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
index 5379b4d3d0944b3a6703b0ed43acefed5c58d53d..2de2d105224b4fe60b67faaf1c48fceb3454e325 100644 (file)
@@ -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);
 
index 8a6a7a3f32f8b691c99a5cfc28d59a0d578c185e..3c54f8d4b6d42112eea434311e7fecca5a320d3e 100644 (file)
@@ -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)
index 6d9eea13f223098f23cd41920e7ebbbb9b8a7dd7..99998cc3c754c1fd3720213271c93808d2affeb0 100644 (file)
@@ -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);
     }
index 42c33d23b9b45a89aad400a0b6cf3e4602d1a076..2eac7b7cc4ae45971d3f2ba39ab7a784e941cb2d 100644 (file)
@@ -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);
index 4c8eda94f1452eb46973cc71b9b8fd7ab4aeaf5a..4579b4c58390a78f70ff07cd33383f8ff609052e 100644 (file)
@@ -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