diff mbox series

Extend thunk support and use it in Ada

Message ID 3718088.xjsADOAp9u@polaris
State New
Headers show
Series Extend thunk support and use it in Ada | expand

Commit Message

Eric Botcazou Sept. 25, 2018, 10:21 a.m. UTC
Hi,

this extends the support for thunks present in the middle-end to accomodate 
the Ada language, which can generate more diverse thunks than C++.  The main 
couple of enhancements are:
 1. Indirect offsets used to implement thunks for self-referential types,
 2. Local thunks used to implement thunks for types defined locally.

It's done entirely in the middle-end, i.e. the interface with the back-end is 
not changed, the rationale being that the vast majority of thunks generated in 
Ada are the same as those generated in C++.

This should be transparent for C++, except for the calls.c change which causes 
thunks not generated by the back-end to use a tailcall even at -O0 when that's 
possible, since that's what most of the back-ends do too.

Tested x86-64/Linux, OK for the mainline?


2018-09-25  Eric Botcazou  <ebotcazou@adacore.com>
            Pierre-Marie de Rodat  <derodat@adacore.com>
            Javier Miranda  <miranda@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.
	* 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.
	* lto-cgraph.c (lto_output_node): Write indirect_offset field.
	(input_node): Write 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/
	* exp_disp.adb (Expand_Interface_Conversion): Use Present test.
	(Expand_Interface_Thunk): Propagate debug info setting from target.
	* exp_util.ads (Find_Interface_Tag): Adjust comment.
	* exp_util.adb (Find_Interface_Tag): Remove assertions of success.
	* sem_res.adb (Resolve_Actuals): If the formal is a class-wide type
 	conversion then do not skip resolving and expanding the conversion.
	* sem_util.adb (Is_Variable_Size_Record): Only look at components
	and robustify the implementation.
	* fe.h (Find_Interface_Tag): Declare.
	(Is_Variable_Size_Record): Likewise.
	* 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.

Comments

Martin Jambor Sept. 25, 2018, 12:31 p.m. UTC | #1
Hi,

On Tue, Sep 25 2018, Eric Botcazou wrote:
> Hi,
>
> this extends the support for thunks present in the middle-end to accomodate 
> the Ada language, which can generate more diverse thunks than C++.  The main 
> couple of enhancements are:
>  1. Indirect offsets used to implement thunks for self-referential types,
>  2. Local thunks used to implement thunks for types defined locally.
>
> It's done entirely in the middle-end, i.e. the interface with the back-end is 
> not changed, the rationale being that the vast majority of thunks generated in 
> Ada are the same as those generated in C++.
>
> This should be transparent for C++, except for the calls.c change which causes 
> thunks not generated by the back-end to use a tailcall even at -O0 when that's 
> possible, since that's what most of the back-ends do too.
>
> Tested x86-64/Linux, OK for the mainline?

I think you should also handle duplicate_thunk_for_node in
cgraphclones.c.  That function clones thunks when the function they
belong to gets cloned.  If you think you don't need to handle it for one
reason or another, at the very least put an assert there that
indirect_offset is zero.

Also...

> Index: cgraphunit.c
> ===================================================================
> --- cgraphunit.c	(revision 264525)
> +++ cgraphunit.c	(working copy)
> @@ -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;
> -	}

does this mean you can have thunks in one compilation unit that belong
to a function in another compilation unit?  Interesting...

> +      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,
>    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);
> +

If I read this correctly (and the offset is an int), nowadays you might
just want to do simple

      stmt = gimple_build_assign (offset_tree,
				  fold_build2 (MEM_REF,
				               integer_type_node,
                                               ptr,
                                               build_int_cst (build_pointer_type (integer_type_node),
                                                              indirect_offset)));

instead of all of the above?  You might then also leave the creation of
vtable_entry_type where it is now.

Thanks,

Martin
Eric Botcazou Sept. 25, 2018, 9:55 p.m. UTC | #2
> I think you should also handle duplicate_thunk_for_node in
> cgraphclones.c.  That function clones thunks when the function they
> belong to gets cloned.  If you think you don't need to handle it for one
> reason or another, at the very least put an assert there that
> indirect_offset is zero.

Thanks for the heads up, now done, as well as in ipa-icf.c.

> does this mean you can have thunks in one compilation unit that belong
> to a function in another compilation unit?  Interesting...

Yes, we can in Ada because the compilation model is different.

> If I read this correctly (and the offset is an int), nowadays you might
> just want to do simple
> 
>       stmt = gimple_build_assign (offset_tree,
> 				  fold_build2 (MEM_REF,
> 				               integer_type_node,
>                                                ptr,
>                                                build_int_cst
> (build_pointer_type (integer_type_node), indirect_offset)));
> 
> instead of all of the above?  You might then also leave the creation of
> vtable_entry_type where it is now.

The new code uses the same pattern as the existing code just above though.
Jeff Law Sept. 28, 2018, 4:43 p.m. UTC | #3
On 9/25/18 4:21 AM, Eric Botcazou wrote:
> Hi,
> 
> this extends the support for thunks present in the middle-end to accomodate 
> the Ada language, which can generate more diverse thunks than C++.  The main 
> couple of enhancements are:
>  1. Indirect offsets used to implement thunks for self-referential types,
>  2. Local thunks used to implement thunks for types defined locally.
> 
> It's done entirely in the middle-end, i.e. the interface with the back-end is 
> not changed, the rationale being that the vast majority of thunks generated in 
> Ada are the same as those generated in C++.
> 
> This should be transparent for C++, except for the calls.c change which causes 
> thunks not generated by the back-end to use a tailcall even at -O0 when that's 
> possible, since that's what most of the back-ends do too.
> 
> Tested x86-64/Linux, OK for the mainline?
> 
> 
> 2018-09-25  Eric Botcazou  <ebotcazou@adacore.com>
>             Pierre-Marie de Rodat  <derodat@adacore.com>
>             Javier Miranda  <miranda@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.
> 	* 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.
> 	* lto-cgraph.c (lto_output_node): Write indirect_offset field.
> 	(input_node): Write 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/
> 	* exp_disp.adb (Expand_Interface_Conversion): Use Present test.
> 	(Expand_Interface_Thunk): Propagate debug info setting from target.
> 	* exp_util.ads (Find_Interface_Tag): Adjust comment.
> 	* exp_util.adb (Find_Interface_Tag): Remove assertions of success.
> 	* sem_res.adb (Resolve_Actuals): If the formal is a class-wide type
>  	conversion then do not skip resolving and expanding the conversion.
> 	* sem_util.adb (Is_Variable_Size_Record): Only look at components
> 	and robustify the implementation.
> 	* fe.h (Find_Interface_Tag): Declare.
> 	(Is_Variable_Size_Record): Likewise.
> 	* 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.
The non-Ada parts look good to me.

Jeff
diff mbox series

Patch

Index: ada/exp_disp.adb
===================================================================
--- ada/exp_disp.adb	(revision 264525)
+++ ada/exp_disp.adb	(working copy)
@@ -1454,7 +1454,7 @@  package body Exp_Disp is
       end if;
 
       Iface_Tag := Find_Interface_Tag (Operand_Typ, Iface_Typ);
-      pragma Assert (Iface_Tag /= Empty);
+      pragma Assert (Present (Iface_Tag));
 
       --  Keep separate access types to interfaces because one internal
       --  function is used to handle the null value (see following comments)
@@ -2046,6 +2046,7 @@  package body Exp_Disp is
       Set_Ekind (Thunk_Id, Ekind (Prim));
       Set_Is_Thunk (Thunk_Id);
       Set_Convention (Thunk_Id, Convention (Prim));
+      Set_Needs_Debug_Info (Thunk_Id, Needs_Debug_Info (Target));
       Set_Thunk_Entity (Thunk_Id, Target);
 
       --  Procedure case
Index: ada/exp_util.adb
===================================================================
--- ada/exp_util.adb	(revision 264525)
+++ ada/exp_util.adb	(working copy)
@@ -5529,7 +5529,6 @@  package body Exp_Util is
          then
             --  Skip the tag associated with the primary table
 
-            pragma Assert (Etype (First_Tag_Component (Typ)) = RTE (RE_Tag));
             AI_Tag := Next_Tag_Component (First_Tag_Component (Typ));
             pragma Assert (Present (AI_Tag));
 
@@ -5590,14 +5589,12 @@  package body Exp_Util is
       --  primary dispatch table.
 
       if Is_Ancestor (Iface, Typ, Use_Full_View => True) then
-         pragma Assert (Etype (First_Tag_Component (Typ)) = RTE (RE_Tag));
          return First_Tag_Component (Typ);
 
       --  Otherwise we need to search for its associated tag component
 
       else
          Find_Tag (Typ);
-         pragma Assert (Found);
          return AI_Tag;
       end if;
    end Find_Interface_Tag;
Index: ada/exp_util.ads
===================================================================
--- ada/exp_util.ads	(revision 264525)
+++ ada/exp_util.ads	(working copy)
@@ -585,8 +585,9 @@  package Exp_Util is
    function Find_Interface_Tag
      (T     : Entity_Id;
       Iface : Entity_Id) return Entity_Id;
-   --  Ada 2005 (AI-251): Given a type T implementing the interface Iface,
-   --  return the record component containing the tag of Iface.
+   --  Ada 2005 (AI-251): Given a type T and an interface Iface, return the
+   --  record component containing the tag of Iface if T implements Iface or
+   --  Empty if it does not.
 
    function Find_Prim_Op (T : Entity_Id; Name : Name_Id) return Entity_Id;
    --  Find the first primitive operation of a tagged type T with name Name.
Index: ada/fe.h
===================================================================
--- ada/fe.h	(revision 264525)
+++ ada/fe.h	(working copy)
@@ -159,8 +159,10 @@  extern void Get_External_Name	(Entity_Id
 /* exp_util: */
 
 #define Is_Fully_Repped_Tagged_Type exp_util__is_fully_repped_tagged_type
+#define Find_Interface_Tag exp_util__find_interface_tag
 
 extern Boolean Is_Fully_Repped_Tagged_Type      (Entity_Id);
+extern Entity_Id Find_Interface_Tag		(Entity_Id, Entity_Id);
 
 /* lib: */
 
@@ -269,12 +271,14 @@  extern Boolean Is_OK_Static_Subtype	(Ent
 #define Defining_Entity			sem_util__defining_entity
 #define First_Actual			sem_util__first_actual
 #define Next_Actual			sem_util__next_actual
+#define Is_Variable_Size_Record 	sem_util__is_variable_size_record
 #define Requires_Transient_Scope	sem_util__requires_transient_scope
 
 extern Entity_Id Defining_Entity	(Node_Id);
 extern Node_Id First_Actual		(Node_Id);
 extern Node_Id Next_Actual		(Node_Id);
-extern Boolean Requires_Transient_Scope (Entity_Id);
+extern Boolean Is_Variable_Size_Record 	(Entity_Id Id);
+extern Boolean Requires_Transient_Scope	(Entity_Id);
 
 /* sinfo: */
 
Index: ada/gcc-interface/decl.c
===================================================================
--- ada/gcc-interface/decl.c	(revision 264525)
+++ ada/gcc-interface/decl.c	(working copy)
@@ -4850,15 +4850,15 @@  is_cplusplus_method (Entity_Id gnat_enti
   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: ada/gcc-interface/trans.c
===================================================================
--- ada/gcc-interface/trans.c	(revision 264525)
+++ ada/gcc-interface/trans.c	(working copy)
@@ -250,6 +250,7 @@  static bool set_end_locus_from_node (tre
 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.  */
@@ -3794,6 +3795,11 @@  Subprogram_Body_to_gnu (Node_Id gnat_nod
   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> ();
@@ -10316,6 +10322,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: ada/gcc-interface/utils.c
===================================================================
--- ada/gcc-interface/utils.c	(revision 264525)
+++ ada/gcc-interface/utils.c	(working copy)
@@ -3293,6 +3293,7 @@  finish_subprog_decl (tree decl, tree asm
 
   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;
 
@@ -3368,9 +3369,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: ada/sem_res.adb
===================================================================
--- ada/sem_res.adb	(revision 264525)
+++ ada/sem_res.adb	(working copy)
@@ -3809,6 +3809,7 @@  package body Sem_Res is
             if Ekind (F) /= E_In_Parameter
               and then Nkind (A) = N_Type_Conversion
               and then not Is_Class_Wide_Type (Etype (Expression (A)))
+              and then not Is_Interface (Etype (A))
             then
                if Ekind (F) = E_In_Out_Parameter
                  and then Is_Array_Type (Etype (F))
Index: ada/sem_util.adb
===================================================================
--- ada/sem_util.adb	(revision 264525)
+++ ada/sem_util.adb	(working copy)
@@ -17714,9 +17714,9 @@  package body Sem_Util is
    begin
       pragma Assert (Is_Record_Type (E));
 
-      Comp := First_Entity (E);
+      Comp := First_Component (E);
       while Present (Comp) loop
-         Comp_Typ := Etype (Comp);
+         Comp_Typ := Underlying_Type (Etype (Comp));
 
          --  Recursive call if the record type has discriminants
 
@@ -17732,7 +17732,7 @@  package body Sem_Util is
             return True;
          end if;
 
-         Next_Entity (Comp);
+         Next_Component (Comp);
       end loop;
 
       return False;
Index: calls.c
===================================================================
--- calls.c	(revision 264525)
+++ calls.c	(working copy)
@@ -3610,9 +3610,8 @@  expand_call (tree exp, rtx target, int i
      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: cgraph.c
===================================================================
--- cgraph.c	(revision 264525)
+++ cgraph.c	(working copy)
@@ -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, t
 
   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: cgraph.h
===================================================================
--- cgraph.h	(revision 264525)
+++ cgraph.h	(working copy)
@@ -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: cgraphunit.c
===================================================================
--- cgraphunit.c	(revision 264525)
+++ cgraphunit.c	(working copy)
@@ -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,
   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_a
   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_a
   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_a
 	  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_a
 		  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_a
       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_a
       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_a
 		}
 
 	      restmp = thunk_adjust (&bsi, restmp, /*this_adjusting=*/0,
-				     fixed_offset, virtual_offset);
+				     fixed_offset, virtual_offset,
+				     indirect_offset);
 	      if (true_label)
 		{
 		  gimple *stmt;
Index: cp/method.c
===================================================================
--- cp/method.c	(revision 264525)
+++ cp/method.c	(working copy)
@@ -375,7 +375,7 @@  use_thunk (tree thunk_fndecl, bool emit_
   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: lto-cgraph.c
===================================================================
--- lto-cgraph.c	(revision 264525)
+++ lto-cgraph.c	(working copy)
@@ -556,6 +556,7 @@  lto_output_node (struct lto_simple_outpu
 	  + (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 *f
       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: tree-inline.c
===================================================================
--- tree-inline.c	(revision 264525)
+++ tree-inline.c	(working copy)
@@ -4473,7 +4473,7 @@  expand_call_inline (basic_block bb, gimp
 			 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: tree-nested.c
===================================================================
--- tree-nested.c	(revision 264525)
+++ tree-nested.c	(working copy)
@@ -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
   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 nesti
      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 nesti
       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 nesti
 
       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_inf
 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_in
   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_no
   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