diff mbox series

Move thunks out of cgraph_node

Message ID 20201023194528.GC19894@kam.mff.cuni.cz
State New
Headers show
Series Move thunks out of cgraph_node | expand

Commit Message

Jan Hubicka Oct. 23, 2020, 7:45 p.m. UTC
Hi,
this patch moves thunk_info out of cgraph_node into a symbol summary.
I also moved it to separate hearder file since cgraph.h became really too
fat.  I plan to contiue with similar breakup in order to cleanup interfaces
and reduce WPA memory footprint (symbol table now consumes more memory than
trees)

Bootstrapped/regtested x86_64-linux, plan to commit it shortly.

gcc/ChangeLog:

2020-10-23  Jan Hubicka  <hubicka@ucw.cz>

	* Makefile.in: Add symtab-thunks.o
	(GTFILES): Add symtab-thunks.h and symtab-thunks.cc; remove cgraphunit.c
	* cgraph.c: Include symtab-thunks.h.
	(cgraph_node::create_thunk): Update
	(symbol_table::create_edge): Update
	(cgraph_node::dump): Update
	(cgraph_node::call_for_symbol_thunks_and_aliases): Update
	(set_nothrow_flag_1): Update
	(set_malloc_flag_1): Update
	(set_const_flag_1): Update
	(collect_callers_of_node_1): Update
	(clone_of_p): Update
	(cgraph_node::verify_node): Update
	(cgraph_node::function_symbol): Update
	(cgraph_c_finalize): Call thunk_info::release.
	(cgraph_node::has_thunk_p): Update
	(cgraph_node::former_thunk_p): Move here from cgraph.h; reimplement.
	* cgraph.h (struct cgraph_thunk_info): Rename to symtab-thunks.h.
	(cgraph_node): Remove thunk field; add thunk bitfield.
	(cgraph_node::expand_thunk): Move to symtab-thunks.h
	(symtab_thunks_cc_finalize): Declare.
	(cgraph_node::has_gimple_body_p): Update.
	(cgraph_node::former_thunk_p): Update.
	* cgraphclones.c: Include symtab-thunks.h.
	(duplicate_thunk_for_node): Update.
	(cgraph_edge::redirect_callee_duplicating_thunks): Update.
	(cgraph_node::expand_all_artificial_thunks): Update.
	(cgraph_node::create_edge_including_clones): Update.
	* cgraphunit.c: Include symtab-thunks.h.
	(vtable_entry_type): Move to symtab-thunks.c.
	(cgraph_node::analyze): Update.
	(analyze_functions): Update.
	(mark_functions_to_output): Update.
	(thunk_adjust): Move to symtab-thunks.c
	(cgraph_node::expand_thunk): Move to symtab-thunks.c
	(cgraph_node::assemble_thunks_and_aliases): Update.
	(output_in_order): Update.
	(cgraphunit_c_finalize): Do not clear vtable_entry_type.
	(cgraph_node::create_wrapper): Update.
	* gengtype.c (open_base_files): Add symtab-thunks.h
	* ipa-comdats.c (propagate_comdat_group): UPdate.
	(ipa_comdats): Update.
	* ipa-cp.c (determine_versionability): UPdate.
	(gather_caller_stats): Update.
	(count_callers): Update
	(set_single_call_flag): Update
	(initialize_node_lattices): Update
	(call_passes_through_thunk_p): Update
	(call_passes_through_thunk): Update
	(propagate_constants_across_call): Update
	(find_more_scalar_values_for_callers_subset): Update
	(has_undead_caller_from_outside_scc_p): Update
	* ipa-fnsummary.c (evaluate_properties_for_edge): Update.
	(compute_fn_summary): Update.
	(inline_analyze_function): Update.
	* ipa-icf.c: Include symtab-thunks.h.
	(sem_function::equals_wpa): Update.
	(redirect_all_callers): Update.
	(sem_function::init): Update.
	(sem_function::parse): Update.
	* ipa-inline-transform.c: Include symtab-thunks.h.
	(inline_call): Update.
	(save_inline_function_body): Update.
	(preserve_function_body_p): Update.
	* ipa-inline.c (inline_small_functions): Update.
	* ipa-polymorphic-call.c: Include alloc-pool.h, symbol-summary.h,
	symtab-thunks.h
	(ipa_polymorphic_call_context::ipa_polymorphic_call_context): Update.
	* ipa-pure-const.c: Include symtab-thunks.h.
	(analyze_function): Update.
	* ipa-sra.c (check_for_caller_issues): Update.
	* ipa-utils.c (ipa_reverse_postorder): Update.
	(ipa_merge_profiles): Update.
	* ipa-visibility.c (non_local_p): Update.
	(cgraph_node::local_p): Update.
	(function_and_variable_visibility): Update.
	* ipa.c (symbol_table::remove_unreachable_nodes): Update.
	* lto-cgraph.c: Include alloc-pool.h, symbol-summary.h and
	symtab-thunks.h
	(lto_output_edge): Update.
	(lto_output_node): Update.
	(compute_ltrans_boundary): Update.
	(output_symtab): Update.
	(verify_node_partition): Update.
	(input_overwrite_node): Update.
	(input_node): Update.
	* lto-streamer-in.c (fixup_call_stmt_edges): Update.
	* symtab-thunks.cc: New file.
	* symtab-thunks.h: New file.
	* toplev.c (toplev::finalize): Call symtab_thunks_cc_finalize.
	* trans-mem.c (ipa_tm_mayenterirr_function): Update.
	(ipa_tm_execute): Update.
	* tree-inline.c (expand_call_inline): Update.
	* tree-nested.c (create_nesting_tree): Update.
	(convert_all_function_calls): Update.
	(gimplify_all_functions): Update.
	* tree-profile.c (tree_profiling): Update.
	* tree-ssa-structalias.c (associate_varinfo_to_alias): Update.
	* tree.c (free_lang_data_in_decl): Update.
	* value-prof.c (init_node_map): Update.

gcc/c-family/ChangeLog:

2020-10-23  Jan Hubicka  <hubicka@ucw.cz>

	* c-common.c (c_common_finalize_early_debug): Update for new thunk api.

gcc/d/ChangeLog:

2020-10-23  Jan Hubicka  <hubicka@ucw.cz>

	* decl.cc (finish_thunk): Update for new thunk api.

gcc/lto/ChangeLog:

2020-10-23  Jan Hubicka  <hubicka@ucw.cz>

	* lto-partition.c (add_symbol_to_partition_1): Update for new thunk
	api.

Comments

mliska Oct. 24, 2020, 6:43 a.m. UTC | #1
On 2020-10-23 21:45, Jan Hubicka wrote:
> Hi,
> this patch moves thunk_info out of cgraph_node into a symbol summary.
> I also moved it to separate hearder file since cgraph.h became really 
> too
> fat.  I plan to contiue with similar breakup in order to cleanup 
> interfaces
> and reduce WPA memory footprint (symbol table now consumes more memory 
> than
> trees)
> 
> Bootstrapped/regtested x86_64-linux, plan to commit it shortly.

Hello.

This broke --enable-checking=release builds, fixed in 
g:a29ff9c53a77b6e208350d8c6db0f3e988f61d1f.

Martin
David Malcolm Nov. 12, 2020, 4:56 p.m. UTC | #2
On Fri, 2020-10-23 at 21:45 +0200, Jan Hubicka wrote:
> Hi,
> this patch moves thunk_info out of cgraph_node into a symbol summary.
> I also moved it to separate hearder file since cgraph.h became really
> too
> fat.  I plan to contiue with similar breakup in order to cleanup
> interfaces
> and reduce WPA memory footprint (symbol table now consumes more
> memory than
> trees)
> 
> Bootstrapped/regtested x86_64-linux, plan to commit it shortly.

This seems to have broken libgccjit (specifically, code that makes
function calls).  Please can you test with --enable-languages=all,jit
(as "jit" isn't part of "all", since it needs --enable-host-shared).

[...snip...]

> +/* Return thunk_info possibly creating new one.  */
> +thunk_info *
> +thunk_info::get_create (cgraph_node *node)
> +{
> +  if (!symtab->m_thunks)
> +    {
> +      symtab->m_thunks
> +	 = new (ggc_alloc_no_dtor <thunk_infos_t> ())
> +	     thunk_infos_t (symtab, true);
> +      symtab->m_thunks->disable_insertion_hook ();
> +    }
> +  return symtab->m_thunks->get_create (node);
> +}

symtab->m_thunks is allocated via ggc_alloc_no_dtor here, thus
allocating it within the GC heap...

[...snip...]

> +/* Free thunk info summaries.  */
> +inline void
> +thunk_info::release ()
> +{
> +  if (symtab->m_thunks)
> +    delete (symtab->m_thunks);
> +  symtab->m_thunks = NULL;
> +}

...but deallocated using plain "delete", attempting to release the GC-
allocated memory into the system heap, leading to an ICE.  This seems
to happen for any compilation of function calls in which
toplev::finalize is called, i.e. any compilation of function calls from
libgccjit.

Does it need to be in the GC heap (maybe for PCH?), or can it be simply
allocated in the system heap via regular "new"?

I hope to get some continuous integration of libgccjit going at some
point (but am focusing on finishing my stage 1 stuff right now, and am
hacking round this for now).

I wonder if it would be useful for debug builds to
call toplev::finalize, so that cc1/cc1plus exercise these cleanup code
paths and thus catch this kind of breakage much earlier.

Dave
Jan Hubicka Nov. 12, 2020, 5:19 p.m. UTC | #3
> On Fri, 2020-10-23 at 21:45 +0200, Jan Hubicka wrote:
> > Hi,
> > this patch moves thunk_info out of cgraph_node into a symbol summary.
> > I also moved it to separate hearder file since cgraph.h became really
> > too
> > fat.  I plan to contiue with similar breakup in order to cleanup
> > interfaces
> > and reduce WPA memory footprint (symbol table now consumes more
> > memory than
> > trees)
> > 
> > Bootstrapped/regtested x86_64-linux, plan to commit it shortly.
> 
> This seems to have broken libgccjit (specifically, code that makes
> function calls).  Please can you test with --enable-languages=all,jit
> (as "jit" isn't part of "all", since it needs --enable-host-shared).

Sorry for that :(
I wl try to keep in mind and test JIT.

> 
> [...snip...]
> 
> > +/* Return thunk_info possibly creating new one.  */
> > +thunk_info *
> > +thunk_info::get_create (cgraph_node *node)
> > +{
> > +  if (!symtab->m_thunks)
> > +    {
> > +      symtab->m_thunks
> > +	 = new (ggc_alloc_no_dtor <thunk_infos_t> ())
> > +	     thunk_infos_t (symtab, true);
> > +      symtab->m_thunks->disable_insertion_hook ();
> > +    }
> > +  return symtab->m_thunks->get_create (node);
> > +}
> 
> symtab->m_thunks is allocated via ggc_alloc_no_dtor here, thus
> allocating it within the GC heap...
> 
> [...snip...]
> 
> > +/* Free thunk info summaries.  */
> > +inline void
> > +thunk_info::release ()
> > +{
> > +  if (symtab->m_thunks)
> > +    delete (symtab->m_thunks);
> > +  symtab->m_thunks = NULL;
> > +}
> 
> ...but deallocated using plain "delete", attempting to release the GC-
> allocated memory into the system heap, leading to an ICE.  This seems
> to happen for any compilation of function calls in which
> toplev::finalize is called, i.e. any compilation of function calls from
> libgccjit.
> 
> Does it need to be in the GC heap (maybe for PCH?), or can it be simply
> allocated in the system heap via regular "new"?

It points to trees (alias decl) and thus it is in PCH so it walks
through it.  In fact I have a cleanup patch for this (which gets rid of
the alias decl that is, in fact, another PCH workaround hack.

> 
> I hope to get some continuous integration of libgccjit going at some
> point (but am focusing on finishing my stage 1 stuff right now, and am
> hacking round this for now).
> 
> I wonder if it would be useful for debug builds to
> call toplev::finalize, so that cc1/cc1plus exercise these cleanup code
> paths and thus catch this kind of breakage much earlier.

I think we could do that for checking compilers.
It would also make it easier to check for memory leaks...

Honza
> 
> Dave
>
diff mbox series

Patch

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 5a8fb0d7612..2174def360c 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1297,6 +1297,7 @@  OBJS = \
 	cfgloopmanip.o \
 	cfgrtl.o \
 	symtab.o \
+	symtab-thunks.o \
 	cgraph.o \
 	cgraphbuild.o \
 	cgraphunit.o \
@@ -2591,6 +2592,7 @@  GTFILES = $(CPPLIB_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
   $(srcdir)/function-abi.h \
   $(srcdir)/output.h $(srcdir)/cfgloop.h $(srcdir)/cfg.h $(srcdir)/profile-count.h \
   $(srcdir)/cselib.h $(srcdir)/basic-block.h  $(srcdir)/ipa-ref.h $(srcdir)/cgraph.h \
+  $(srcdir)/symtab-thunks.h $(srcdir)/symtab-thunks.cc \
   $(srcdir)/reload.h $(srcdir)/caller-save.c $(srcdir)/symtab.c \
   $(srcdir)/alias.c $(srcdir)/bitmap.c $(srcdir)/cselib.c $(srcdir)/cgraph.c \
   $(srcdir)/ipa-prop.c $(srcdir)/ipa-cp.c $(srcdir)/ipa-utils.h \
@@ -2629,7 +2631,7 @@  GTFILES = $(CPPLIB_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
   $(srcdir)/omp-offload.c \
   $(srcdir)/omp-general.c \
   $(srcdir)/omp-low.c \
-  $(srcdir)/targhooks.c $(out_file) $(srcdir)/passes.c $(srcdir)/cgraphunit.c \
+  $(srcdir)/targhooks.c $(out_file) $(srcdir)/passes.c \
   $(srcdir)/cgraphclones.c \
   $(srcdir)/tree-phinodes.c \
   $(srcdir)/tree-ssa-alias.h \
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index e16ca3894bc..7a26df75907 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -9132,7 +9132,7 @@  c_common_finalize_early_debug (void)
      functions that are still reachable at this point.  */
   struct cgraph_node *cnode;
   FOR_EACH_FUNCTION (cnode)
-    if (!cnode->alias && !cnode->thunk.thunk_p
+    if (!cnode->alias && !cnode->thunk
 	&& (cnode->has_gimple_body_p () || !DECL_IS_BUILTIN (cnode->decl)))
       (*debug_hooks->early_global_decl) (cnode->decl);
 }
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 067984d773c..9f317733c91 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -65,6 +65,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "tree-into-ssa.h"
 #include "ipa-inline.h"
 #include "tree-nested.h"
+#include "symtab-thunks.h"
 
 /* FIXME: Only for PROP_loops, but cgraph shouldn't have to know about this.  */
 #include "tree-pass.h"
@@ -629,15 +630,17 @@  cgraph_node::create_thunk (tree alias, tree, bool this_adjusting,
 		       ? virtual_value == wi::to_wide (virtual_offset)
 		       : virtual_value == 0);
 
-  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;
-  node->thunk.thunk_p = true;
+  node->thunk = true;
   node->definition = true;
 
+  thunk_info *i = thunk_info::get_create (node);
+  i->fixed_offset = fixed_offset;
+  i->virtual_value = virtual_value;
+  i->indirect_offset = indirect_offset;
+  i->alias = real_alias;
+  i->this_adjusting = this_adjusting;
+  i->virtual_offset_p = virtual_offset != NULL;
+
   return node;
 }
 
@@ -910,7 +913,7 @@  symbol_table::create_edge (cgraph_node *caller, cgraph_node *callee,
       = decl_maybe_in_construction_p (NULL, NULL, call_stmt,
 				      caller->decl);
   else
-    edge->in_polymorphic_cdtor = caller->thunk.thunk_p;
+    edge->in_polymorphic_cdtor = caller->thunk;
   if (callee)
     caller->calls_declare_variant_alt |= callee->declare_variant_alt;
 
@@ -2173,37 +2176,17 @@  cgraph_node::dump (FILE *f)
 
   fprintf (f, "\n");
 
-  if (thunk.thunk_p)
+  if (thunk)
     {
       fprintf (f, "  Thunk");
-      if (thunk.alias)
-	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 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);
+      thunk_info::get (this)->dump (f);
     }
   else if (former_thunk_p ())
-    fprintf (f, "  Former thunk 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
-      && DECL_P (thunk.alias))
     {
-      fprintf (f, "  Alias of %s",
-	       lang_hooks.decl_printable_name (thunk.alias, 2));
-      if (DECL_ASSEMBLER_NAME_SET_P (thunk.alias))
-	fprintf (f, " (asm:%s)",
-		 IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk.alias)));
-      fprintf (f, "\n");
+      fprintf (f, "  Former thunk ");
+      thunk_info::get (this)->dump (f);
     }
+  else gcc_checking_assert (!thunk_info::get (this));
   
   fprintf (f, "  Called by: ");
 
@@ -2444,11 +2427,11 @@  cgraph_node::call_for_symbol_thunks_and_aliases (bool (*callback)
   if (avail <= AVAIL_INTERPOSABLE)
     return false;
   for (e = callers; e; e = e->next_caller)
-    if (e->caller->thunk.thunk_p
+    if (e->caller->thunk
 	&& (include_overwritable
 	    || e->caller->get_availability () > AVAIL_INTERPOSABLE)
 	&& !(exclude_virtual_thunks
-	     && e->caller->thunk.virtual_offset_p))
+	     && thunk_info::get (e->caller)->virtual_offset_p))
       if (e->caller->call_for_symbol_thunks_and_aliases (callback, data,
 						       include_overwritable,
 						       exclude_virtual_thunks))
@@ -2522,7 +2505,7 @@  set_nothrow_flag_1 (cgraph_node *node, bool nothrow, bool non_call,
 	set_nothrow_flag_1 (alias, nothrow, non_call, changed);
     }
   for (cgraph_edge *e = node->callers; e; e = e->next_caller)
-    if (e->caller->thunk.thunk_p
+    if (e->caller->thunk
 	&& (!nothrow || e->caller->get_availability () > AVAIL_INTERPOSABLE))
       set_nothrow_flag_1 (e->caller, nothrow, non_call, changed);
 }
@@ -2571,7 +2554,7 @@  set_malloc_flag_1 (cgraph_node *node, bool malloc_p, bool *changed)
     }
 
   for (cgraph_edge *e = node->callers; e; e = e->next_caller)
-    if (e->caller->thunk.thunk_p
+    if (e->caller->thunk
 	&& (!malloc_p || e->caller->get_availability () > AVAIL_INTERPOSABLE))
       set_malloc_flag_1 (e->caller, malloc_p, changed);
 }
@@ -2690,13 +2673,13 @@  set_const_flag_1 (cgraph_node *node, bool set_const, bool looping,
 	set_const_flag_1 (alias, set_const, looping, changed);
     }
   for (cgraph_edge *e = node->callers; e; e = e->next_caller)
-    if (e->caller->thunk.thunk_p
+    if (e->caller->thunk
 	&& (!set_const || e->caller->get_availability () > AVAIL_INTERPOSABLE))
       {
 	/* Virtual thunks access virtual offset in the vtable, so they can
 	   only be pure, never const.  */
         if (set_const
-	    && (e->caller->thunk.virtual_offset_p
+	    && (thunk_info::get (e->caller)->virtual_offset_p
 	        || !node->binds_to_current_def_p (e->caller)))
 	  *changed |= e->caller->set_pure_flag (true, looping);
 	else
@@ -3040,7 +3023,7 @@  collect_callers_of_node_1 (cgraph_node *node, void *data)
   if (avail > AVAIL_INTERPOSABLE)
     for (cs = node->callers; cs != NULL; cs = cs->next_caller)
       if (!cs->indirect_inlining_edge
-	  && !cs->caller->thunk.thunk_p)
+	  && !cs->caller->thunk)
         redirect_callers->safe_push (cs);
   return false;
 }
@@ -3071,7 +3054,7 @@  clone_of_p (cgraph_node *node, cgraph_node *node2)
       || node2->former_clone_of == node->decl)
     return true;
 
-  if (!node->thunk.thunk_p && !node->former_thunk_p ())
+  if (!node->thunk && !node->former_thunk_p ())
     {
       while (node2 && node->decl != node2->decl)
 	node2 = node2->clone_of;
@@ -3081,9 +3064,9 @@  clone_of_p (cgraph_node *node, cgraph_node *node2)
   /* There are no virtual clones of thunks so check former_clone_of or if we
      might have skipped thunks because this adjustments are no longer
      necessary.  */
-  while (node->thunk.thunk_p || node->former_thunk_p ())
+  while (node->thunk || node->former_thunk_p ())
     {
-      if (!node->thunk.this_adjusting)
+      if (!thunk_info::get (node)->this_adjusting)
 	return false;
       /* In case of instrumented expanded thunks, which can have multiple calls
 	 in them, we do not know how to continue and just have to be
@@ -3647,7 +3630,7 @@  cgraph_node::verify_node (void)
 	}
     }
 
-  if (analyzed && thunk.thunk_p)
+  if (analyzed && thunk)
     {
       if (!callees)
 	{
@@ -3831,7 +3814,7 @@  cgraph_node::function_symbol (enum availability *availability,
 {
   cgraph_node *node = ultimate_alias_target (availability, ref);
 
-  while (node->thunk.thunk_p)
+  while (node->thunk)
     {
       enum availability a;
 
@@ -3858,7 +3841,7 @@  cgraph_node::function_or_virtual_thunk_symbol
 {
   cgraph_node *node = ultimate_alias_target (availability, ref);
 
-  while (node->thunk.thunk_p && !node->thunk.virtual_offset_p)
+  while (node->thunk && !thunk_info::get (node)->virtual_offset_p)
     {
       enum availability a;
 
@@ -4007,6 +3990,7 @@  void
 cgraph_c_finalize (void)
 {
   nested_function_info::release ();
+  thunk_info::release ();
   symtab = NULL;
 
   x_cgraph_nodes_queue = NULL;
@@ -4042,7 +4026,7 @@  bool
 cgraph_node::has_thunk_p (cgraph_node *node, void *)
 {
   for (cgraph_edge *e = node->callers; e; e = e->next_caller)
-    if (e->caller->thunk.thunk_p)
+    if (e->caller->thunk)
       return true;
   return false;
 }
@@ -4178,6 +4162,21 @@  cgraph_c_tests ()
 
 } // namespace selftest
 
+/* Return true if this node represents a former, i.e. an expanded, thunk.  */
+
+bool
+cgraph_node::former_thunk_p (void)
+{
+  if (thunk)
+    return false;
+  thunk_info *i = thunk_info::get (this);
+  if (!i)
+    return false;
+  gcc_checking_assert (i->fixed_offset || i->virtual_offset_p
+		       || i->indirect_offset);
+  return true;
+}
+
 #endif /* CHECKING_P */
 
 #include "gt-cgraph.h"
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 6c450342764..9eb48d5b62f 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -681,52 +681,6 @@  extern const char * const cgraph_availability_names[];
 extern const char * const ld_plugin_symbol_resolution_names[];
 extern const char * const tls_model_names[];
 
-/* Sub-structure of cgraph_node.  Holds information about thunk, used only for
-   same body aliases.
-
-   Thunks are basically wrappers around methods which are introduced in case
-   of multiple inheritance in order to adjust the value of the "this" pointer
-   or of the returned value.
-
-   In the case of this-adjusting thunks, each back-end can override the
-   can_output_mi_thunk/output_mi_thunk target hooks to generate a minimal thunk
-   (with a tail call for instance) directly as assembly.  For the default hook
-   or for the case where the can_output_mi_thunk hooks return false, the thunk
-   is gimplified and lowered using the regular machinery.  */
-
-struct GTY(()) cgraph_thunk_info {
-  /* Offset used to adjust "this".  */
-  HOST_WIDE_INT fixed_offset;
-
-  /* Offset in the virtual table to get the offset to adjust "this".  Valid iff
-     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;
-
-  /* Nonzero for a "this" adjusting thunk and zero for a result adjusting
-     thunk.  */
-  bool this_adjusting;
-
-  /* If true, this thunk is what we call a virtual thunk.  In this case:
-     * for this-adjusting thunks, after the FIXED_OFFSET based adjustment is
-       done, add to the result the offset found in the vtable at:
-	 vptr + VIRTUAL_VALUE
-     * for result-adjusting thunks, the FIXED_OFFSET adjustment is done after
-       the virtual one.  */
-  bool virtual_offset_p;
-
-  /* Set to true when alias node (the cgraph_node to which this struct belong)
-     is a thunk.  Access to any other fields is invalid if this is false.  */
-  bool thunk_p;
-};
-
 /* Represent which DECL tree (or reference to such tree)
    will be replaced by another tree while versioning.  */
 struct GTY(()) ipa_replace_map
@@ -925,10 +879,11 @@  struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
       next_sibling_clone (NULL), prev_sibling_clone (NULL), clones (NULL),
       clone_of (NULL), call_site_hash (NULL), former_clone_of (NULL),
       simdclone (NULL), simd_clones (NULL), ipa_transforms_to_apply (vNULL),
-      inlined_to (NULL), rtl (NULL), clone (), thunk (),
+      inlined_to (NULL), rtl (NULL), clone (),
       count (profile_count::uninitialized ()),
       count_materialization_scale (REG_BR_PROB_BASE), profile_id (0),
-      unit_id (0), tp_first_run (0), used_as_abstract_origin (false),
+      unit_id (0), tp_first_run (0), thunk (false),
+      used_as_abstract_origin (false),
       lowered (false), process (false), frequency (NODE_FREQUENCY_NORMAL),
       only_called_at_startup (false), only_called_at_exit (false),
       tm_clone (false), dispatcher_function (false), calls_comdat_local (false),
@@ -1078,7 +1033,7 @@  struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
 
   /* Add thunk alias into callgraph.  The alias declaration is ALIAS and it
      aliases DECL with an adjustments made into the first parameter.
-     See comments in struct cgraph_thunk_info for detail on the parameters.  */
+     See comments in struct symtab-thunks.h for detail on the parameters.  */
   cgraph_node * create_thunk (tree alias, tree, bool this_adjusting,
 			      HOST_WIDE_INT fixed_offset,
 			      HOST_WIDE_INT virtual_value,
@@ -1099,13 +1054,6 @@  struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
   cgraph_node *ultimate_alias_target (availability *availability = NULL,
 				      symtab_node *ref = NULL);
 
-  /* Expand thunk NODE to gimple if possible.
-     When FORCE_GIMPLE_THUNK is true, gimple thunk is created and
-     no assembler is produced.
-     When OUTPUT_ASM_THUNK is true, also produce assembler for
-     thunks that are not lowered.  */
-  bool expand_thunk (bool output_asm_thunks, bool force_gimple_thunk);
-
   /*  Call expand_thunk on all callers that are thunks and analyze those
       nodes that were expanded.  */
   void expand_all_artificial_thunks ();
@@ -1324,7 +1272,7 @@  struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
   inline bool has_gimple_body_p (void);
 
   /* Return true if this node represents a former, i.e. an expanded, thunk.  */
-  inline bool former_thunk_p (void);
+  bool former_thunk_p (void);
 
   /* Check if function calls comdat local.  This is used to recompute
      calls_comdat_local flag after function transformations.  */
@@ -1462,7 +1410,6 @@  struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
 
   struct cgraph_rtl_info *rtl;
   cgraph_clone_info clone;
-  cgraph_thunk_info thunk;
 
   /* Expected number of executions: calculated in profile.c.  */
   profile_count count;
@@ -1476,6 +1423,8 @@  struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
   /* Time profiler: first run of function.  */
   int tp_first_run;
 
+  /* True when symbol is a thunk.  */
+  unsigned thunk : 1;
   /* Set when decl is an abstract function pointed to by the
      ABSTRACT_DECL_ORIGIN of a reachable function.  */
   unsigned used_as_abstract_origin : 1;
@@ -2241,6 +2190,10 @@  struct asmname_hasher : ggc_ptr_hash <symtab_node>
   static bool equal (symtab_node *n, const_tree t);
 };
 
+struct thunk_info;
+template <class T> class function_summary;
+typedef function_summary <thunk_info *> thunk_summary;
+
 class GTY((tag ("SYMTAB"))) symbol_table
 {
 public:
@@ -2257,6 +2210,7 @@  public:
   function_flags_ready (false), cpp_implicit_aliases_done (false),
   section_hash (NULL), assembler_name_hash (NULL), init_priority_hash (NULL),
   dump_file (NULL), ipa_clones_dump_file (NULL), cloned_nodes (),
+  m_thunks (NULL),
   m_first_edge_removal_hook (NULL), m_first_cgraph_removal_hook (NULL),
   m_first_edge_duplicated_hook (NULL), m_first_cgraph_duplicated_hook (NULL),
   m_first_cgraph_insertion_hook (NULL), m_first_varpool_insertion_hook (NULL),
@@ -2538,6 +2492,9 @@  public:
 
   hash_set <const cgraph_node *> GTY ((skip)) cloned_nodes;
 
+  /* Thunk annotations.  */
+  thunk_summary *m_thunks;
+
 private:
   /* Allocate a cgraph_edge structure and fill it with data according to the
      parameters of which only CALLEE can be NULL (when creating an indirect
@@ -2612,6 +2569,9 @@  cgraph_inline_failed_type_t cgraph_inline_failed_type (cgraph_inline_failed_t);
 void cgraphunit_c_finalize (void);
 int tp_first_run_node_cmp (const void *pa, const void *pb);
 
+/* In symtab-thunks.cc  */
+void symtab_thunks_cc_finalize (void);
+
 /*  Initialize datastructures so DECL is a function in lowered gimple form.
     IN_SSA is true if the gimple is in SSA.  */
 basic_block init_lowered_empty_function (tree, bool, profile_count);
@@ -3082,18 +3042,7 @@  symbol_table::next_function_with_gimple_body (cgraph_node *node)
 inline bool
 cgraph_node::has_gimple_body_p (void)
 {
-  return definition && !thunk.thunk_p && !alias;
-}
-
-/* Return true if this node represents a former, i.e. an expanded, thunk.  */
-
-inline bool
-cgraph_node::former_thunk_p (void)
-{
-  return (!thunk.thunk_p
-	  && (thunk.fixed_offset
-	      || thunk.virtual_offset_p
-	      || thunk.indirect_offset));
+  return definition && !thunk && !alias;
 }
 
 /* Walk all functions with body defined.  */
diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
index 07a51a58aef..41c6efb10ac 100644
--- a/gcc/cgraphclones.c
+++ b/gcc/cgraphclones.c
@@ -85,6 +85,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "tree-vrp.h"
 #include "ipa-prop.h"
 #include "ipa-fnsummary.h"
+#include "symtab-thunks.h"
 
 /* Create clone of edge in the node N represented by CALL_EXPR
    the callgraph.  */
@@ -183,28 +184,28 @@  duplicate_thunk_for_node (cgraph_node *thunk, cgraph_node *node)
   cgraph_node *new_thunk, *thunk_of;
   thunk_of = thunk->callees->callee->ultimate_alias_target ();
 
-  if (thunk_of->thunk.thunk_p)
+  if (thunk_of->thunk)
     node = duplicate_thunk_for_node (thunk_of, node);
 
   if (!DECL_ARGUMENTS (thunk->decl))
     thunk->get_untransformed_body ();
 
+  thunk_info *i = thunk_info::get (thunk);
   cgraph_edge *cs;
   for (cs = node->callers; cs; cs = cs->next_caller)
-    if (cs->caller->thunk.thunk_p
-	&& cs->caller->thunk.fixed_offset == thunk->thunk.fixed_offset
-	&& 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;
+    if (cs->caller->thunk)
+      {
+	thunk_info *i2 = thunk_info::get (cs->caller);
+	if (*i2 == *i)
+	  return cs->caller;
+      }
 
   tree new_decl;
   if (node->clone.param_adjustments)
     {
       /* We do not need to duplicate this_adjusting thunks if we have removed
 	 this.  */
-      if (thunk->thunk.this_adjusting
+      if (i->this_adjusting
 	  && !node->clone.param_adjustments->first_param_intact_p ())
 	return node;
 
@@ -256,7 +257,7 @@  void
 cgraph_edge::redirect_callee_duplicating_thunks (cgraph_node *n)
 {
   cgraph_node *orig_to = callee->ultimate_alias_target ();
-  if (orig_to->thunk.thunk_p)
+  if (orig_to->thunk)
     n = duplicate_thunk_for_node (orig_to, n);
 
   redirect_callee (n);
@@ -270,14 +271,14 @@  cgraph_node::expand_all_artificial_thunks ()
 {
   cgraph_edge *e;
   for (e = callers; e;)
-    if (e->caller->thunk.thunk_p)
+    if (e->caller->thunk)
       {
 	cgraph_node *thunk = e->caller;
 
 	e = e->next_caller;
-	if (thunk->expand_thunk (false, false))
+	if (expand_thunk (thunk, false, false))
 	  {
-	    thunk->thunk.thunk_p = false;
+	    thunk->thunk = false;
 	    thunk->analyze ();
 	    ipa_analyze_node (thunk);
 	    inline_analyze_function (thunk);
@@ -812,7 +813,7 @@  cgraph_node::create_edge_including_clones (cgraph_node *callee,
   if (node)
     while (node != this)
       /* Thunk clones do not get updated while copying inline function body.  */
-      if (!node->thunk.thunk_p)
+      if (!node->thunk)
 	{
 	  cgraph_edge *edge = node->get_edge (old_stmt);
 
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 1e2262789dd..d2d98c8dc8a 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -207,6 +207,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "attribs.h"
 #include "ipa-inline.h"
 #include "omp-offload.h"
+#include "symtab-thunks.h"
 
 /* Queue of cgraph nodes scheduled to be added into cgraph.  This is a
    secondary queue used during optimization to accommodate passes that
@@ -217,9 +218,6 @@  static void expand_all_functions (void);
 static void mark_functions_to_output (void);
 static void handle_alias_pairs (void);
 
-/* Used for vtable lookup in thunk adjusting.  */
-static GTY (()) tree vtable_entry_type;
-
 /* Return true if this symbol is a function from the C frontend specified
    directly in RTL form (with "__RTL").  */
 
@@ -619,9 +617,10 @@  cgraph_node::analyze (void)
   location_t saved_loc = input_location;
   input_location = DECL_SOURCE_LOCATION (decl);
 
-  if (thunk.thunk_p)
+  if (thunk)
     {
-      cgraph_node *t = cgraph_node::get (thunk.alias);
+      thunk_info *info = thunk_info::get (this);
+      cgraph_node *t = cgraph_node::get (info->alias);
 
       create_edge (t, NULL, t->count);
       callees->can_throw_external = !TREE_NOTHROW (t->decl);
@@ -635,8 +634,8 @@  cgraph_node::analyze (void)
 	  if (!t->analyzed && t->definition)
 	    t->analyze ();
 	}
-      bool ret = expand_thunk (false, false);
-      thunk.alias = NULL;
+      bool ret = expand_thunk (this, false, false);
+      thunk_info::get (this)->alias = NULL;
       if (!ret)
 	return;
     }
@@ -1222,7 +1221,7 @@  analyze_functions (bool first_time)
 	      See gcc.c-torture/compile/20011119-1.c  */
 	      if (!DECL_STRUCT_FUNCTION (decl)
 		  && !cnode->alias
-		  && !cnode->thunk.thunk_p
+		  && !cnode->thunk
 		  && !cnode->dispatcher_function)
 		{
 		  cnode->reset ();
@@ -1377,10 +1376,10 @@  analyze_functions (bool first_time)
 
 	  if (cnode->definition && !gimple_has_body_p (decl)
 	      && !cnode->alias
-	      && !cnode->thunk.thunk_p)
+	      && !cnode->thunk)
 	    cnode->reset ();
 
-	  gcc_assert (!cnode->definition || cnode->thunk.thunk_p
+	  gcc_assert (!cnode->definition || cnode->thunk
 		      || cnode->alias
 		      || gimple_has_body_p (decl)
 		      || cnode->native_rtl_p ());
@@ -1611,7 +1610,7 @@  mark_functions_to_output (void)
 	 always inlined, as well as those that are reachable from
 	 outside the current compilation unit.  */
       if (node->analyzed
-	  && !node->thunk.thunk_p
+	  && !node->thunk
 	  && !node->alias
 	  && !node->inlined_to
 	  && !TREE_ASM_WRITTEN (decl)
@@ -1624,7 +1623,7 @@  mark_functions_to_output (void)
 	      for (next = dyn_cast<cgraph_node *> (node->same_comdat_group);
 		   next != node;
 		   next = dyn_cast<cgraph_node *> (next->same_comdat_group))
-		if (!next->thunk.thunk_p && !next->alias
+		if (!next->thunk && !next->alias
 		    && !next->comdat_local_p ())
 		  next->process = 1;
 	    }
@@ -1733,487 +1732,6 @@  init_lowered_empty_function (tree decl, bool in_ssa, profile_count count)
   return bb;
 }
 
-/* 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 indirect_offset)
-{
-  gassign *stmt;
-  tree ret;
-
-  if (this_adjusting
-      && fixed_offset != 0)
-    {
-      stmt = gimple_build_assign
-		(ptr, fold_build_pointer_plus_hwi_loc (input_location,
-						       ptr,
-						       fixed_offset));
-      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)
-    {
-      tree vtabletmp;
-      tree vtabletmp2;
-      tree vtabletmp3;
-
-      vtabletmp =
-	create_tmp_reg (build_pointer_type
-			  (build_pointer_type (vtable_entry_type)), "vptr");
-
-      /* The vptr is always at offset zero in the object.  */
-      stmt = gimple_build_assign (vtabletmp,
-				  build1 (NOP_EXPR, TREE_TYPE (vtabletmp),
-					  ptr));
-      gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
-
-      /* Form the vtable address.  */
-      vtabletmp2 = create_tmp_reg (TREE_TYPE (TREE_TYPE (vtabletmp)),
-				     "vtableaddr");
-      stmt = gimple_build_assign (vtabletmp2,
-				  build_simple_mem_ref (vtabletmp));
-      gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
-
-      /* Find the entry with the vcall offset.  */
-      stmt = gimple_build_assign (vtabletmp2,
-				  fold_build_pointer_plus_loc (input_location,
-							       vtabletmp2,
-							       virtual_offset));
-      gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
-
-      /* Get the offset itself.  */
-      vtabletmp3 = create_tmp_reg (TREE_TYPE (TREE_TYPE (vtabletmp2)),
-				     "vcalloffset");
-      stmt = gimple_build_assign (vtabletmp3,
-				  build_simple_mem_ref (vtabletmp2));
-      gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
-
-      /* Adjust the `this' pointer.  */
-      ptr = fold_build_pointer_plus_loc (input_location, ptr, vtabletmp3);
-      ptr = force_gimple_operand_gsi (bsi, ptr, true, NULL_TREE, false,
-				      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.  */
-    {
-      tree ptrtmp;
-
-      if (VAR_P (ptr))
-        ptrtmp = ptr;
-      else
-        {
-          ptrtmp = create_tmp_reg (TREE_TYPE (ptr), "ptr");
-          stmt = gimple_build_assign (ptrtmp, ptr);
-	  gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
-	}
-      ptr = fold_build_pointer_plus_hwi_loc (input_location,
-					     ptrtmp, fixed_offset);
-    }
-
-  /* Emit the statement and gimplify the adjustment expression.  */
-  ret = create_tmp_reg (TREE_TYPE (ptr), "adjusted_this");
-  stmt = gimple_build_assign (ret, ptr);
-  gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
-
-  return ret;
-}
-
-/* Expand thunk NODE to gimple if possible.
-   When FORCE_GIMPLE_THUNK is true, gimple thunk is created and
-   no assembler is produced.
-   When OUTPUT_ASM_THUNK is true, also produce assembler for
-   thunks that are not lowered.  */
-
-bool
-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;
-  tree a;
-
-  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))
-    {
-      tree fn_block;
-      tree restype = TREE_TYPE (TREE_TYPE (thunk_fndecl));
-
-      if (!output_asm_thunks)
-	{
-	  analyzed = true;
-	  return false;
-	}
-
-      if (in_lto_p)
-	get_untransformed_body ();
-      a = DECL_ARGUMENTS (thunk_fndecl);
-      
-      current_function_decl = thunk_fndecl;
-
-      /* Ensure thunks are emitted in their correct sections.  */
-      resolve_unique_section (thunk_fndecl, 0,
-			      flag_function_sections);
-
-      DECL_RESULT (thunk_fndecl)
-	= build_decl (DECL_SOURCE_LOCATION (thunk_fndecl),
-		      RESULT_DECL, 0, restype);
-      DECL_CONTEXT (DECL_RESULT (thunk_fndecl)) = thunk_fndecl;
-
-      /* The back end expects DECL_INITIAL to contain a BLOCK, so we
-	 create one.  */
-      fn_block = make_node (BLOCK);
-      BLOCK_VARS (fn_block) = a;
-      DECL_INITIAL (thunk_fndecl) = fn_block;
-      BLOCK_SUPERCONTEXT (fn_block) = thunk_fndecl;
-      allocate_struct_function (thunk_fndecl, false);
-      init_function_start (thunk_fndecl);
-      cfun->is_thunk = 1;
-      insn_locations_init ();
-      set_curr_insn_location (DECL_SOURCE_LOCATION (thunk_fndecl));
-      prologue_location = curr_insn_location ();
-
-      targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl,
-				       fixed_offset, virtual_value, alias);
-
-      insn_locations_finalize ();
-      init_insn_lengths ();
-      free_after_compilation (cfun);
-      TREE_ASM_WRITTEN (thunk_fndecl) = 1;
-      thunk.thunk_p = false;
-      analyzed = false;
-    }
-  else if (stdarg_p (TREE_TYPE (thunk_fndecl)))
-    {
-      error ("generic thunk code fails for method %qD which uses %<...%>",
-	     thunk_fndecl);
-      TREE_ASM_WRITTEN (thunk_fndecl) = 1;
-      analyzed = true;
-      return false;
-    }
-  else
-    {
-      tree restype;
-      basic_block bb, then_bb, else_bb, return_bb;
-      gimple_stmt_iterator bsi;
-      int nargs = 0;
-      tree arg;
-      int i;
-      tree resdecl;
-      tree restmp = NULL;
-
-      gcall *call;
-      greturn *ret;
-      bool alias_is_noreturn = TREE_THIS_VOLATILE (alias);
-
-      /* We may be called from expand_thunk that releases body except for
-	 DECL_ARGUMENTS.  In this case force_gimple_thunk is true.  */
-      if (in_lto_p && !force_gimple_thunk)
-	get_untransformed_body ();
-
-      /* We need to force DECL_IGNORED_P when the thunk is created
-	 after early debug was run.  */
-      if (force_gimple_thunk)
-	DECL_IGNORED_P (thunk_fndecl) = 1;
-
-      a = DECL_ARGUMENTS (thunk_fndecl);
-
-      current_function_decl = thunk_fndecl;
-
-      /* Ensure thunks are emitted in their correct sections.  */
-      resolve_unique_section (thunk_fndecl, 0,
-			      flag_function_sections);
-
-      bitmap_obstack_initialize (NULL);
-
-      if (thunk.virtual_offset_p)
-        virtual_offset = size_int (virtual_value);
-
-      /* Build the return declaration for the function.  */
-      restype = TREE_TYPE (TREE_TYPE (thunk_fndecl));
-      if (DECL_RESULT (thunk_fndecl) == NULL_TREE)
-	{
-	  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;
-	}
-      else
-	resdecl = DECL_RESULT (thunk_fndecl);
-
-      profile_count cfg_count = count;
-      if (!cfg_count.initialized_p ())
-	cfg_count = profile_count::from_gcov_type (BB_FREQ_MAX).guessed_local ();
-
-      bb = then_bb = else_bb = return_bb
-	= init_lowered_empty_function (thunk_fndecl, true, cfg_count);
-
-      bsi = gsi_start_bb (bb);
-
-      /* Build call to the function being thunked.  */
-      if (!VOID_TYPE_P (restype)
-	  && (!alias_is_noreturn
-	      || TREE_ADDRESSABLE (restype)
-	      || TREE_CODE (TYPE_SIZE_UNIT (restype)) != INTEGER_CST))
-	{
-	  if (DECL_BY_REFERENCE (resdecl))
-	    {
-	      restmp = gimple_fold_indirect_ref (resdecl);
-	      if (!restmp)
-		restmp = build2 (MEM_REF,
-				 TREE_TYPE (TREE_TYPE (resdecl)),
-				 resdecl,
-				 build_int_cst (TREE_TYPE (resdecl), 0));
-	    }
-	  else if (!is_gimple_reg_type (restype))
-	    {
-	      if (aggregate_value_p (resdecl, TREE_TYPE (thunk_fndecl)))
-		{
-		  restmp = resdecl;
-
-		  if (VAR_P (restmp))
-		    {
-		      add_local_decl (cfun, restmp);
-		      BLOCK_VARS (DECL_INITIAL (current_function_decl))
-			= restmp;
-		    }
-		}
-	      else
-		restmp = create_tmp_var (restype, "retval");
-	    }
-	  else
-	    restmp = create_tmp_reg (restype, "retval");
-	}
-
-      for (arg = a; arg; arg = DECL_CHAIN (arg))
-        nargs++;
-      auto_vec<tree> vargs (nargs);
-      i = 0;
-      arg = a;
-      if (this_adjusting)
-	{
-	  vargs.quick_push (thunk_adjust (&bsi, a, 1, fixed_offset,
-					  virtual_offset, indirect_offset));
-	  arg = DECL_CHAIN (a);
-	  i = 1;
-	}
-
-      if (nargs)
-	for (; i < nargs; i++, arg = DECL_CHAIN (arg))
-	  {
-	    tree tmp = arg;
-	    DECL_NOT_GIMPLE_REG_P (arg) = 0;
-	    if (!is_gimple_val (arg))
-	      {
-		tmp = create_tmp_reg (TYPE_MAIN_VARIANT
-				      (TREE_TYPE (arg)), "arg");
-		gimple *stmt = gimple_build_assign (tmp, arg);
-		gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
-	      }
-	    vargs.quick_push (tmp);
-	  }
-      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 required to
-         return values with DECL_BY_REFERENCE.  */
-      if (aggregate_value_p (resdecl, TREE_TYPE (thunk_fndecl))
-	  && (!is_gimple_reg_type (TREE_TYPE (resdecl))
-	      || DECL_BY_REFERENCE (resdecl)))
-        gimple_call_set_return_slot_opt (call, true);
-
-      if (restmp)
-	{
-          gimple_call_set_lhs (call, restmp);
-	  gcc_assert (useless_type_conversion_p (TREE_TYPE (restmp),
-						 TREE_TYPE (TREE_TYPE (alias))));
-	}
-      gsi_insert_after (&bsi, call, GSI_NEW_STMT);
-      if (!alias_is_noreturn)
-	{
-	  if (restmp && !this_adjusting
-	      && (fixed_offset || virtual_offset))
-	    {
-	      tree true_label = NULL_TREE;
-
-	      if (TREE_CODE (TREE_TYPE (restmp)) == POINTER_TYPE)
-		{
-		  gimple *stmt;
-		  edge e;
-		  /* If the return type is a pointer, we need to
-		     protect against NULL.  We know there will be an
-		     adjustment, because that's why we're emitting a
-		     thunk.  */
-		  then_bb = create_basic_block (NULL, bb);
-		  then_bb->count = cfg_count - cfg_count.apply_scale (1, 16);
-		  return_bb = create_basic_block (NULL, then_bb);
-		  return_bb->count = cfg_count;
-		  else_bb = create_basic_block (NULL, else_bb);
-		  else_bb->count = cfg_count.apply_scale (1, 16);
-		  add_bb_to_loop (then_bb, bb->loop_father);
-		  add_bb_to_loop (return_bb, bb->loop_father);
-		  add_bb_to_loop (else_bb, bb->loop_father);
-		  remove_edge (single_succ_edge (bb));
-		  true_label = gimple_block_label (then_bb);
-		  stmt = gimple_build_cond (NE_EXPR, restmp,
-					    build_zero_cst (TREE_TYPE (restmp)),
-					    NULL_TREE, NULL_TREE);
-		  gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
-		  e = make_edge (bb, then_bb, EDGE_TRUE_VALUE);
-		  e->probability = profile_probability::guessed_always ()
-					.apply_scale (1, 16);
-		  e = make_edge (bb, else_bb, EDGE_FALSE_VALUE);
-		  e->probability = profile_probability::guessed_always ()
-					.apply_scale (1, 16);
-		  make_single_succ_edge (return_bb,
-					 EXIT_BLOCK_PTR_FOR_FN (cfun), 0);
-		  make_single_succ_edge (then_bb, return_bb, EDGE_FALLTHRU);
-		  e = make_edge (else_bb, return_bb, EDGE_FALLTHRU);
-		  e->probability = profile_probability::always ();
-		  bsi = gsi_last_bb (then_bb);
-		}
-
-	      restmp = thunk_adjust (&bsi, restmp, /*this_adjusting=*/0,
-				     fixed_offset, virtual_offset,
-				     indirect_offset);
-	      if (true_label)
-		{
-		  gimple *stmt;
-		  bsi = gsi_last_bb (else_bb);
-		  stmt = gimple_build_assign (restmp,
-					      build_zero_cst (TREE_TYPE (restmp)));
-		  gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
-		  bsi = gsi_last_bb (return_bb);
-		}
-	    }
-	  else
-	    {
-	      gimple_call_set_tail (call, true);
-	      cfun->tail_call_marked = true;
-	    }
-
-	  /* Build return value.  */
-	  if (!DECL_BY_REFERENCE (resdecl))
-	    ret = gimple_build_return (restmp);
-	  else
-	    ret = gimple_build_return (resdecl);
-
-	  gsi_insert_after (&bsi, ret, GSI_NEW_STMT);
-	}
-      else
-	{
-	  gimple_call_set_tail (call, true);
-	  cfun->tail_call_marked = true;
-	  remove_edge (single_succ_edge (bb));
-	}
-
-      cfun->gimple_df->in_ssa_p = true;
-      update_max_bb_count ();
-      profile_status_for_fn (cfun)
-        = cfg_count.initialized_p () && cfg_count.ipa_p ()
-	  ? PROFILE_READ : PROFILE_GUESSED;
-      /* FIXME: C++ FE should stop setting TREE_ASM_WRITTEN on thunks.  */
-      TREE_ASM_WRITTEN (thunk_fndecl) = false;
-      delete_unreachable_blocks ();
-      update_ssa (TODO_update_ssa);
-      checking_verify_flow_info ();
-      free_dominance_info (CDI_DOMINATORS);
-
-      /* Since we want to emit the thunk, we explicitly mark its name as
-	 referenced.  */
-      thunk.thunk_p = false;
-      lowered = true;
-      bitmap_obstack_release (NULL);
-    }
-  current_function_decl = NULL;
-  set_cfun (NULL);
-  return true;
-}
-
 /* Assemble thunks and aliases associated to node.  */
 
 void
@@ -2223,13 +1741,13 @@  cgraph_node::assemble_thunks_and_aliases (void)
   ipa_ref *ref;
 
   for (e = callers; e;)
-    if (e->caller->thunk.thunk_p
+    if (e->caller->thunk
 	&& !e->caller->inlined_to)
       {
 	cgraph_node *thunk = e->caller;
 
 	e = e->next_caller;
-	thunk->expand_thunk (true, false);
+	expand_thunk (thunk, true, false);
 	thunk->assemble_thunks_and_aliases ();
       }
     else
@@ -2594,7 +2112,7 @@  output_in_order (void)
   cgraph_order_sort *node;
 
   FOR_EACH_DEFINED_FUNCTION (cnode)
-    if (cnode->process && !cnode->thunk.thunk_p
+    if (cnode->process && !cnode->thunk
 	&& !cnode->alias && cnode->no_reorder)
       nodes.safe_push (cgraph_order_sort (cnode));
 
@@ -3034,7 +2552,6 @@  cgraphunit_c_finalize (void)
   gcc_assert (cgraph_new_nodes.length () == 0);
   cgraph_new_nodes.truncate (0);
 
-  vtable_entry_type = NULL;
   queued_nodes = &symtab_terminator;
 
   first_analyzed = NULL;
@@ -3064,8 +2581,11 @@  cgraph_node::create_wrapper (cgraph_node *target)
   /* Turn alias into thunk and expand it into GIMPLE representation.  */
   definition = true;
 
-  memset (&thunk, 0, sizeof (cgraph_thunk_info));
-  thunk.thunk_p = true;
+  /* Create empty thunk, but be sure we did not keep former thunk around.
+     In that case we would need to preserve the info.  */
+  gcc_checking_assert (!thunk_info::get (this));
+  thunk_info::get_create (this);
+  thunk = true;
   create_edge (target, NULL, count);
   callees->can_throw_external = !TREE_NOTHROW (target->decl);
 
@@ -3077,11 +2597,10 @@  cgraph_node::create_wrapper (cgraph_node *target)
       arguments = TREE_CHAIN (arguments);
     }
 
-  expand_thunk (false, true);
+  expand_thunk (this, false, true);
+  thunk_info::remove (this);
 
   /* Inline summary set-up.  */
   analyze ();
   inline_analyze_function (this);
 }
-
-#include "gt-cgraphunit.h"
diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc
index 1167462c305..457894fbe22 100644
--- a/gcc/d/decl.cc
+++ b/gcc/d/decl.cc
@@ -54,6 +54,9 @@  along with GCC; see the file COPYING3.  If not see
 #include "debug.h"
 #include "tree-pretty-print.h"
 #include "tree-nested.h"
+#include "alloc-pool.h"
+#include "symbol-summary.h"
+#include "symtab-thunks.h"
 
 #include "d-tree.h"
 
@@ -1702,7 +1705,7 @@  finish_thunk (tree thunk, tree function)
       if (!stdarg_p (TREE_TYPE (thunk)))
 	{
 	  thunk_node->create_edge (funcn, NULL, thunk_node->count);
-	  thunk_node->expand_thunk (false, true);
+	  expand_thunk (thunk_node, false, true);
 	}
 
       /* Tell the back-end to not bother inlining the function, this is
diff --git a/gcc/gengtype.c b/gcc/gengtype.c
index a59a8823f82..dd21ade5795 100644
--- a/gcc/gengtype.c
+++ b/gcc/gengtype.c
@@ -1726,7 +1726,8 @@  open_base_files (void)
       "except.h", "output.h",  "cfgloop.h", "target.h", "lto-streamer.h",
       "target-globals.h", "ipa-ref.h", "cgraph.h", "symbol-summary.h",
       "ipa-prop.h", "ipa-fnsummary.h", "dwarf2out.h", "omp-general.h",
-      "omp-offload.h", "ipa-modref-tree.h", "ipa-modref.h", NULL
+      "omp-offload.h", "ipa-modref-tree.h", "ipa-modref.h", "symtab-thunks.h",
+      NULL
     };
     const char *const *ifp;
     outf_p gtype_desc_c;
diff --git a/gcc/ipa-comdats.c b/gcc/ipa-comdats.c
index feea458ca3f..350c474d8c6 100644
--- a/gcc/ipa-comdats.c
+++ b/gcc/ipa-comdats.c
@@ -128,7 +128,7 @@  propagate_comdat_group (struct symtab_node *symbol,
 	if (cgraph_node * cn = dyn_cast <cgraph_node *> (symbol2))
 	  {
 	    /* Thunks cannot call across section boundary.  */
-	    if (cn->thunk.thunk_p)
+	    if (cn->thunk)
 	      newgroup = propagate_comdat_group (symbol2, newgroup, map);
 	    /* If we see inline clone, its comdat group actually
 	       corresponds to the comdat group of the function it
@@ -344,7 +344,7 @@  ipa_comdats (void)
       if (!symbol->get_comdat_group ()
 	  && !symbol->alias
 	  && (!(fun = dyn_cast <cgraph_node *> (symbol))
-	      || !fun->thunk.thunk_p)
+	      || !fun->thunk)
 	  && symbol->real_symbol_p ())
 	{
 	  tree *val = map.get (symbol);
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 2152f9e5876..f981366a345 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -595,7 +595,7 @@  determine_versionability (struct cgraph_node *node,
   /* There are a number of generic reasons functions cannot be versioned.  We
      also cannot remove parameters if there are type attributes such as fnspec
      present.  */
-  if (node->alias || node->thunk.thunk_p)
+  if (node->alias || node->thunk)
     reason = "alias or thunk";
   else if (!node->versionable)
     reason = "not a tree_versionable_function";
@@ -646,7 +646,7 @@  determine_versionability (struct cgraph_node *node,
 	    reason = "external function which calls va_arg_pack_len";
         }
 
-  if (reason && dump_file && !node->alias && !node->thunk.thunk_p)
+  if (reason && dump_file && !node->alias && !node->thunk)
     fprintf (dump_file, "Function %s is not versionable, reason: %s.\n",
 	     node->dump_name (), reason);
 
@@ -691,7 +691,7 @@  gather_caller_stats (struct cgraph_node *node, void *data)
   struct cgraph_edge *cs;
 
   for (cs = node->callers; cs; cs = cs->next_caller)
-    if (!cs->caller->thunk.thunk_p)
+    if (!cs->caller->thunk)
       {
         if (cs->count.ipa ().initialized_p ())
 	  stats->count_sum += cs->count.ipa ();
@@ -1155,7 +1155,7 @@  count_callers (cgraph_node *node, void *data)
   for (cgraph_edge *cs = node->callers; cs; cs = cs->next_caller)
     /* Local thunks can be handled transparently, but if the thunk cannot
        be optimized out, count it as a real use.  */
-    if (!cs->caller->thunk.thunk_p || !cs->caller->local)
+    if (!cs->caller->thunk || !cs->caller->local)
       ++*caller_count;
   return false;
 }
@@ -1168,7 +1168,7 @@  set_single_call_flag (cgraph_node *node, void *)
 {
   cgraph_edge *cs = node->callers;
   /* Local thunks can be handled transparently, skip them.  */
-  while (cs && cs->caller->thunk.thunk_p && cs->caller->local)
+  while (cs && cs->caller->thunk && cs->caller->local)
     cs = cs->next_caller;
   if (cs && IPA_NODE_REF (cs->caller))
     {
@@ -1215,7 +1215,7 @@  initialize_node_lattices (struct cgraph_node *node)
     }
 
   if (dump_file && (dump_flags & TDF_DETAILS)
-      && !node->alias && !node->thunk.thunk_p)
+      && !node->alias && !node->thunk)
     {
       fprintf (dump_file, "Initializing lattices of %s\n",
 	       node->dump_name ());
@@ -1240,7 +1240,7 @@  initialize_node_lattices (struct cgraph_node *node)
       node->clone.param_adjustments->get_surviving_params (&surviving_params);
 
       if (dump_file && (dump_flags & TDF_DETAILS)
-	  && !node->alias && !node->thunk.thunk_p)
+	  && !node->alias && !node->thunk)
 	{
 	  bool first = true;
 	  for (int j = 0; j < ipa_get_param_count (info); j++)
@@ -2806,12 +2806,12 @@  propagate_aggs_across_jump_function (struct cgraph_edge *cs,
    non-thunk) destination, the call passes through a thunk.  */
 
 static bool
-call_passes_through_thunk_p (cgraph_edge *cs)
+call_passes_through_thunk (cgraph_edge *cs)
 {
   cgraph_node *alias_or_thunk = cs->callee;
   while (alias_or_thunk->alias)
     alias_or_thunk = alias_or_thunk->get_alias_target ();
-  return alias_or_thunk->thunk.thunk_p;
+  return alias_or_thunk->thunk;
 }
 
 /* Propagate constants from the caller to the callee of CS.  INFO describes the
@@ -2853,7 +2853,7 @@  propagate_constants_across_call (struct cgraph_edge *cs)
   /* If this call goes through a thunk we must not propagate to the first (0th)
      parameter.  However, we might need to uncover a thunk from below a series
      of aliases first.  */
-  if (call_passes_through_thunk_p (cs))
+  if (call_passes_through_thunk (cs))
     {
       ret |= set_all_contains_variable (ipa_get_parm_lattices (callee_info,
 							       0));
@@ -4698,7 +4698,7 @@  find_more_scalar_values_for_callers_subset (struct cgraph_node *node,
 	  if (!IPA_EDGE_REF (cs)
 	      || i >= ipa_get_cs_argument_count (IPA_EDGE_REF (cs))
 	      || (i == 0
-		  && call_passes_through_thunk_p (cs)))
+		  && call_passes_through_thunk (cs)))
 	    {
 	      newval = NULL_TREE;
 	      break;
@@ -5661,7 +5661,7 @@  has_undead_caller_from_outside_scc_p (struct cgraph_node *node,
   struct cgraph_edge *cs;
 
   for (cs = node->callers; cs; cs = cs->next_caller)
-    if (cs->caller->thunk.thunk_p
+    if (cs->caller->thunk
 	&& cs->caller->call_for_symbol_thunks_and_aliases
 	  (has_undead_caller_from_outside_scc_p, NULL, true))
       return true;
diff --git a/gcc/ipa-fnsummary.c b/gcc/ipa-fnsummary.c
index f680e4221e7..f27c5f07fa7 100644
--- a/gcc/ipa-fnsummary.c
+++ b/gcc/ipa-fnsummary.c
@@ -699,7 +699,7 @@  evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
 	       }
 	  }
 	else
-	  gcc_assert (!count || callee->thunk.thunk_p);
+	  gcc_assert (!count || callee->thunk);
     }
   else if (e->call_stmt && !e->call_stmt_cannot_inline_p && info->conds)
     {
@@ -3051,12 +3051,12 @@  compute_fn_summary (struct cgraph_node *node, bool early)
   class ipa_size_summary *size_info = ipa_size_summaries->get_create (node);
 
   /* Estimate the stack size for the function if we're optimizing.  */
-  self_stack_size = optimize && !node->thunk.thunk_p
+  self_stack_size = optimize && !node->thunk
 		    ? estimated_stack_frame_size (node) : 0;
   size_info->estimated_self_stack_size = self_stack_size;
   info->estimated_stack_size = self_stack_size;
 
-  if (node->thunk.thunk_p)
+  if (node->thunk)
     {
       ipa_call_summary *es = ipa_call_summaries->get_create (node->callees);
       predicate t = true;
@@ -4221,7 +4221,7 @@  inline_analyze_function (struct cgraph_node *node)
 
   if (dump_file)
     fprintf (dump_file, "\nAnalyzing function: %s\n", node->dump_name ());
-  if (opt_for_fn (node->decl, optimize) && !node->thunk.thunk_p)
+  if (opt_for_fn (node->decl, optimize) && !node->thunk)
     inline_indirect_intraprocedural_analysis (node);
   compute_fn_summary (node, false);
   if (!optimize)
diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
index d2b4e781389..8cae076b3ce 100644
--- a/gcc/ipa-icf.c
+++ b/gcc/ipa-icf.c
@@ -84,6 +84,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "stor-layout.h"
 #include "dbgcnt.h"
 #include "tree-vector-builder.h"
+#include "symtab-thunks.h"
 
 using namespace ipa_icf_gimple;
 
@@ -530,22 +531,14 @@  sem_function::equals_wpa (sem_item *item,
 
   m_compared_func = static_cast<sem_function *> (item);
 
-  if (cnode->thunk.thunk_p != cnode2->thunk.thunk_p)
-    return return_false_with_msg ("thunk_p mismatch");
+  if (cnode->thunk != cnode2->thunk)
+    return return_false_with_msg ("thunk mismatch");
+  if (cnode->former_thunk_p () != cnode2->former_thunk_p ())
+    return return_false_with_msg ("former_thunk_p mismatch");
 
-  if (cnode->thunk.thunk_p)
-    {
-      if (cnode->thunk.fixed_offset != cnode2->thunk.fixed_offset)
-        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)
-        return return_false_with_msg ("thunk virtual_offset_p mismatch");
-    }
+  if ((cnode->thunk || cnode->former_thunk_p ())
+      && thunk_info::get (cnode) != thunk_info::get (cnode2))
+    return return_false_with_msg ("thunk_info mismatch");
 
   /* Compare special function DECL attributes.  */
   if (DECL_FUNCTION_PERSONALITY (decl)
@@ -968,7 +961,7 @@  redirect_all_callers (cgraph_node *n, cgraph_node *to)
       /* Redirecting thunks to interposable symbols or symbols in other sections
 	 may not be supported by target output code.  Play safe for now and
 	 punt on redirection.  */
-      if (!e->caller->thunk.thunk_p)
+      if (!e->caller->thunk)
 	{
 	  struct cgraph_edge *nexte = e->next_caller;
           e->redirect_callee (to);
@@ -1362,7 +1355,7 @@  sem_function::init (ipa_icf_gimple::func_checker *checker)
 
   edge_count = n_edges_for_fn (func);
   cgraph_node *cnode = dyn_cast <cgraph_node *> (node);
-  if (!cnode->thunk.thunk_p)
+  if (!cnode->thunk)
     {
       cfg_checksum = coverage_compute_cfg_checksum (func);
 
@@ -1407,12 +1400,7 @@  sem_function::init (ipa_icf_gimple::func_checker *checker)
   else
     {
       cfg_checksum = 0;
-      inchash::hash hstate;
-      hstate.add_hwi (cnode->thunk.fixed_offset);
-      hstate.add_hwi (cnode->thunk.virtual_value);
-      hstate.add_flag (cnode->thunk.this_adjusting);
-      hstate.add_flag (cnode->thunk.virtual_offset_p);
-      gcode_hash = hstate.end ();
+      gcode_hash = thunk_info::get (cnode)->hash ();
     }
 
   m_checker = NULL;
@@ -1494,7 +1482,7 @@  sem_function::parse (cgraph_node *node, bitmap_obstack *stack,
   tree fndecl = node->decl;
   function *func = DECL_STRUCT_FUNCTION (fndecl);
 
-  if (!func || (!node->has_gimple_body_p () && !node->thunk.thunk_p))
+  if (!func || (!node->has_gimple_body_p () && !node->thunk))
     return NULL;
 
   if (lookup_attribute_by_prefix ("omp ", DECL_ATTRIBUTES (node->decl)) != NULL)
diff --git a/gcc/ipa-inline-transform.c b/gcc/ipa-inline-transform.c
index f419df04961..3782cce12e3 100644
--- a/gcc/ipa-inline-transform.c
+++ b/gcc/ipa-inline-transform.c
@@ -50,6 +50,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "ipa-utils.h"
 #include "ipa-modref-tree.h"
 #include "ipa-modref.h"
+#include "symtab-thunks.h"
 
 int ncalls_inlined;
 int nfunctions_inlined;
@@ -353,14 +354,18 @@  inline_call (struct cgraph_edge *e, bool update_original,
   to = e->caller;
   if (to->inlined_to)
     to = to->inlined_to;
-  if (to->thunk.thunk_p)
+  if (to->thunk)
     {
       struct cgraph_node *target = to->callees->callee;
       thunk_expansion = true;
+
+      /* Remove all annotations, but keep thunk info.  */
+      thunk_info info = *thunk_info::get (to);
       symtab->call_cgraph_removal_hooks (to);
+      *thunk_info::get_create (to) = info;
       if (in_lto_p)
 	to->get_untransformed_body ();
-      to->expand_thunk (false, true);
+      expand_thunk (to, false, true);
       /* When thunk is instrumented we may have multiple callees.  */
       for (e = to->callees; e && e->callee != target; e = e->next_callee)
 	;
@@ -564,9 +569,9 @@  save_inline_function_body (struct cgraph_node *node)
   first_clone = node->clones;
 
   /* Arrange first clone to not be thunk as those do not have bodies.  */
-  if (first_clone->thunk.thunk_p)
+  if (first_clone->thunk)
     {
-      while (first_clone->thunk.thunk_p)
+      while (first_clone->thunk)
         first_clone = first_clone->next_sibling_clone;
       first_clone->prev_sibling_clone->next_sibling_clone
 	= first_clone->next_sibling_clone;
@@ -673,11 +678,11 @@  static bool
 preserve_function_body_p (struct cgraph_node *node)
 {
   gcc_assert (symtab->global_info_ready);
-  gcc_assert (!node->alias && !node->thunk.thunk_p);
+  gcc_assert (!node->alias && !node->thunk);
 
   /* Look if there is any non-thunk clone around.  */
   for (node = node->clones; node; node = node->next_sibling_clone)
-    if (!node->thunk.thunk_p)
+    if (!node->thunk)
       return true;
   return false;
 }
diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
index 3827970635c..8db23457dae 100644
--- a/gcc/ipa-inline.c
+++ b/gcc/ipa-inline.c
@@ -1947,7 +1947,7 @@  inline_small_functions (void)
     if (!node->inlined_to)
       {
 	if (!node->alias && node->analyzed
-	    && (node->has_gimple_body_p () || node->thunk.thunk_p)
+	    && (node->has_gimple_body_p () || node->thunk)
 	    && opt_for_fn (node->decl, optimize))
 	  {
 	    class ipa_fn_summary *info = ipa_fn_summaries->get (node);
diff --git a/gcc/ipa-polymorphic-call.c b/gcc/ipa-polymorphic-call.c
index 65516b6d7b2..326aef7fd6a 100644
--- a/gcc/ipa-polymorphic-call.c
+++ b/gcc/ipa-polymorphic-call.c
@@ -38,6 +38,9 @@  along with GCC; see the file COPYING3.  If not see
 #include "tree-dfa.h"
 #include "gimple-pretty-print.h"
 #include "tree-into-ssa.h"
+#include "alloc-pool.h"
+#include "symbol-summary.h"
+#include "symtab-thunks.h"
 
 /* Return true when TYPE contains an polymorphic type and thus is interesting
    for devirtualization machinery.  */
@@ -1007,8 +1010,9 @@  ipa_polymorphic_call_context::ipa_polymorphic_call_context (tree fndecl,
 	     Because we walked all the way to the beginning of thunk, we now
 	     see pointer &bar-thunk.fixed_offset and need to compensate
 	     for it.  */
-	  if (node->thunk.fixed_offset)
-	    offset -= node->thunk.fixed_offset * BITS_PER_UNIT;
+	  thunk_info *info = thunk_info::get (node);
+	  if (info && info->fixed_offset)
+	    offset -= info->fixed_offset * BITS_PER_UNIT;
 
 	  /* Dynamic casting has possibly upcasted the type
 	     in the hierarchy.  In this case outer type is less
@@ -1021,7 +1025,7 @@  ipa_polymorphic_call_context::ipa_polymorphic_call_context (tree fndecl,
 	      /* If we compile thunk with virtual offset, the THIS pointer
 		 is adjusted by unknown value.  We can't thus use outer info
 		 at all.  */
-	      || node->thunk.virtual_offset_p)
+	      || (info && info->virtual_offset_p))
 	    {
 	      outer_type = NULL;
 	      if (instance)
@@ -1047,10 +1051,10 @@  ipa_polymorphic_call_context::ipa_polymorphic_call_context (tree fndecl,
 	    }
 	  if (instance)
 	    {
+	      thunk_info *info = thunk_info::get (node);
 	      /* If method is expanded thunk, we need to apply thunk offset
 		 to instance pointer.  */
-	      if (node->thunk.virtual_offset_p
-		  || node->thunk.fixed_offset)
+	      if (info && (info->virtual_offset_p || info->fixed_offset))
 		*instance = NULL;
 	      else
 	        *instance = base_pointer;
diff --git a/gcc/ipa-pure-const.c b/gcc/ipa-pure-const.c
index 70930a5eac0..054f35981a5 100644
--- a/gcc/ipa-pure-const.c
+++ b/gcc/ipa-pure-const.c
@@ -61,6 +61,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "symbol-summary.h"
 #include "ipa-prop.h"
 #include "ipa-fnsummary.h"
+#include "symtab-thunks.h"
 
 /* Lattice values for const and pure functions.  Everything starts out
    being const, then may drop to pure and then neither depending on
@@ -1025,11 +1026,11 @@  analyze_function (struct cgraph_node *fn, bool ipa)
 		    flags_from_decl_or_type (fn->decl),
 		    fn->cannot_return_p ());
 
-  if (fn->thunk.thunk_p || fn->alias)
+  if (fn->thunk || fn->alias)
     {
       /* Thunk gets propagated through, so nothing interesting happens.  */
       gcc_assert (ipa);
-      if (fn->thunk.thunk_p && fn->thunk.virtual_offset_p)
+      if (fn->thunk && thunk_info::get (fn)->virtual_offset_p)
 	l->pure_const_state = IPA_NEITHER;
       return l;
     }
diff --git a/gcc/ipa-sra.c b/gcc/ipa-sra.c
index 03e3fc55daf..8d4f580e009 100644
--- a/gcc/ipa-sra.c
+++ b/gcc/ipa-sra.c
@@ -2935,7 +2935,7 @@  check_for_caller_issues (struct cgraph_node *node, void *data)
 
   for (cgraph_edge *cs = node->callers; cs; cs = cs->next_caller)
     {
-      if (cs->caller->thunk.thunk_p)
+      if (cs->caller->thunk)
 	{
 	  issues->thunk = true;
 	  /* TODO: We should be able to process at least some types of
diff --git a/gcc/ipa-utils.c b/gcc/ipa-utils.c
index 23e7f714306..2945380f20a 100644
--- a/gcc/ipa-utils.c
+++ b/gcc/ipa-utils.c
@@ -297,7 +297,7 @@  ipa_reverse_postorder (struct cgraph_node **order)
 	  && (pass
 	      || (!node->address_taken
 		  && !node->inlined_to
-		  && !node->alias && !node->thunk.thunk_p
+		  && !node->alias && !node->thunk
 		  && !node->only_called_directly_p ())))
 	{
 	  stack_size = 0;
@@ -460,8 +460,8 @@  ipa_merge_profiles (struct cgraph_node *dst,
     }
 
   /* First handle functions with no gimple body.  */
-  if (dst->thunk.thunk_p || dst->alias
-      || src->thunk.thunk_p || src->alias)
+  if (dst->thunk || dst->alias
+      || src->thunk || src->alias)
     {
       scale_ipa_profile_for_fn (dst, orig_count);
       return;
diff --git a/gcc/ipa-visibility.c b/gcc/ipa-visibility.c
index 7c854f471e8..97d93889c86 100644
--- a/gcc/ipa-visibility.c
+++ b/gcc/ipa-visibility.c
@@ -95,7 +95,7 @@  non_local_p (struct cgraph_node *node, void *data ATTRIBUTE_UNUSED)
   return !(node->only_called_directly_or_aliased_p ()
 	   /* i386 would need update to output thunk with local calling
 	      conventions.  */
-	   && !node->thunk.thunk_p
+	   && !node->thunk
 	   && node->definition
 	   && !DECL_EXTERNAL (node->decl)
 	   && !lookup_attribute ("noipa", DECL_ATTRIBUTES (node->decl))
@@ -112,7 +112,7 @@  cgraph_node::local_p (void)
 {
    cgraph_node *n = ultimate_alias_target ();
 
-   if (n->thunk.thunk_p)
+   if (n->thunk)
      return n->callees->callee->local_p ();
    return !n->call_for_symbol_thunks_and_aliases (non_local_p,
 						  NULL, true);
@@ -729,7 +729,7 @@  function_and_variable_visibility (bool whole_program)
 	  && !DECL_EXTERNAL (node->decl))
 	localize_node (whole_program, node);
 
-      if (node->thunk.thunk_p
+      if (node->thunk
 	  && TREE_PUBLIC (node->decl))
 	{
 	  struct cgraph_node *decl_node = node;
diff --git a/gcc/ipa.c b/gcc/ipa.c
index ab7256d857f..cd0facf9956 100644
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -475,7 +475,7 @@  symbol_table::remove_unreachable_nodes (FILE *file)
 		}
 
 	    }
-	  else if (cnode->thunk.thunk_p)
+	  else if (cnode->thunk)
 	    enqueue_node (cnode->callees->callee, &first, &reachable);
 
 	  /* If any reachable function has simd clones, mark them as
@@ -525,7 +525,7 @@  symbol_table::remove_unreachable_nodes (FILE *file)
 	  /* We keep definitions of thunks and aliases in the boundary so
 	     we can walk to the ultimate alias targets and function symbols
 	     reliably.  */
-	  if (node->alias || node->thunk.thunk_p)
+	  if (node->alias || node->thunk)
 	    ;
 	  else if (!body_needed_for_clonning.contains (node->decl))
 	    {
@@ -537,7 +537,7 @@  symbol_table::remove_unreachable_nodes (FILE *file)
 	    }
 	  else if (!node->clone_of)
 	    gcc_assert (in_lto_p || DECL_RESULT (node->decl));
-	  if (node->definition && !node->alias && !node->thunk.thunk_p)
+	  if (node->definition && !node->alias && !node->thunk)
 	    {
 	      if (file)
 		fprintf (file, " %s", node->dump_name ());
@@ -547,7 +547,7 @@  symbol_table::remove_unreachable_nodes (FILE *file)
 	      node->cpp_implicit_alias = false;
 	      node->alias = false;
 	      node->transparent_alias = false;
-	      node->thunk.thunk_p = false;
+	      node->thunk = false;
 	      node->weakref = false;
 	      /* After early inlining we drop always_inline attributes on
 		 bodies of functions that are still referenced (have their
diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
index 93a99f3465b..19d4ca54e92 100644
--- a/gcc/lto-cgraph.c
+++ b/gcc/lto-cgraph.c
@@ -39,6 +39,9 @@  along with GCC; see the file COPYING3.  If not see
 #include "omp-offload.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "alloc-pool.h"
+#include "symbol-summary.h"
+#include "symtab-thunks.h"
 
 /* True when asm nodes has been output.  */
 bool asm_nodes_output = false;
@@ -261,7 +264,7 @@  lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
 			 : gimple_uid (edge->call_stmt) + 1;
   bp_pack_enum (&bp, cgraph_inline_failed_t,
 	        CIF_N_REASONS, edge->inline_failed);
-  gcc_checking_assert (uid || edge->caller->thunk.thunk_p);
+  gcc_checking_assert (uid || edge->caller->thunk);
   bp_pack_var_len_unsigned (&bp, uid);
   bp_pack_value (&bp, edge->speculative_id, 16);
   bp_pack_value (&bp, edge->indirect_inlining_edge, 1);
@@ -393,7 +396,7 @@  lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node,
   boundary_p = !lto_symtab_encoder_in_partition_p (encoder, node);
 
   if (node->analyzed && (!boundary_p || node->alias
-			 || (node->thunk.thunk_p && !node->inlined_to)))
+			 || (node->thunk && !node->inlined_to)))
     tag = LTO_symtab_analyzed_node;
   else
     tag = LTO_symtab_unavail_node;
@@ -533,10 +536,19 @@  lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node,
   bp_pack_value (&bp, node->nonfreeing_fn, 1);
   bp_pack_value (&bp, node->merged_comdat, 1);
   bp_pack_value (&bp, node->merged_extern_inline, 1);
-  bp_pack_value (&bp, node->thunk.thunk_p, 1);
+  bp_pack_value (&bp, node->thunk, 1);
   bp_pack_value (&bp, node->parallelized_function, 1);
   bp_pack_value (&bp, node->declare_variant_alt, 1);
   bp_pack_value (&bp, node->calls_declare_variant_alt, 1);
+
+  /* Stream thunk info always because we use it in
+     ipa_polymorphic_call_context::ipa_polymorphic_call_context
+     to properly interpret THIS pointers for thunks that has been converted
+     to Gimple.  */
+  struct thunk_info *thunk = node->definition ? thunk_info::get (node) : NULL;
+
+  bp_pack_value (&bp, thunk != NULL, 1);
+
   bp_pack_enum (&bp, ld_plugin_symbol_resolution,
 	        LDPR_NUM_KNOWN,
 		/* When doing incremental link, we will get new resolution
@@ -546,26 +558,15 @@  lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node,
   streamer_write_bitpack (&bp);
   streamer_write_data_stream (ob->main_stream, section, strlen (section) + 1);
 
-  /* Stream thunk info always because we use it in
-     ipa_polymorphic_call_context::ipa_polymorphic_call_context
-     to properly interpret THIS pointers for thunks that has been converted
-     to Gimple.  */
-  if (node->definition)
-    {
-      streamer_write_uhwi_stream
-	 (ob->main_stream,
-	  1 + (node->thunk.this_adjusting != 0) * 2
-	  + (node->thunk.virtual_offset_p != 0) * 4);
-      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);
   streamer_write_hwi_stream (ob->main_stream, node->unit_id);
   if (DECL_STATIC_CONSTRUCTOR (node->decl))
     streamer_write_hwi_stream (ob->main_stream, node->get_init_priority ());
   if (DECL_STATIC_DESTRUCTOR (node->decl))
     streamer_write_hwi_stream (ob->main_stream, node->get_fini_priority ());
+
+  if (thunk)
+    thunk_info::get (node)->stream_out (ob);
 }
 
 /* Output the varpool NODE to OB. 
@@ -931,7 +932,7 @@  compute_ltrans_boundary (lto_symtab_encoder_t in_encoder)
       if (node->alias && node->analyzed)
 	create_references (encoder, node);
       if (cnode
-	  && cnode->thunk.thunk_p && !cnode->inlined_to)
+	  && cnode->thunk && !cnode->inlined_to)
 	add_node_to (encoder, cnode->callees->callee, false);
       while (node->transparent_alias && node->analyzed)
 	{
@@ -987,7 +988,7 @@  output_symtab (void)
     {
       node = dyn_cast <cgraph_node *> (lto_symtab_encoder_deref (encoder, i));
       if (node
-	  && ((node->thunk.thunk_p && !node->inlined_to)
+	  && ((node->thunk && !node->inlined_to)
 	      || lto_symtab_encoder_in_partition_p (encoder, node)))
 	{
 	  output_outgoing_cgraph_edges (node->callees, ob, encoder);
@@ -1139,13 +1140,14 @@  verify_node_partition (symtab_node *node)
    NODE or to replace the values in it, for instance because the first
    time we saw it, the function body was not available but now it
    is.  BP is a bitpack with all the bitflags for NODE read from the
-   stream.  */
+   stream.  Initialize HAS_THUNK_INFO to indicate if thunk info should
+   be streamed in.  */
 
 static void
 input_overwrite_node (struct lto_file_decl_data *file_data,
 		      struct cgraph_node *node,
 		      enum LTO_symtab_tags tag,
-		      struct bitpack_d *bp)
+		      struct bitpack_d *bp, bool *has_thunk_info)
 {
   node->aux = (void *) tag;
   node->lto_file_data = file_data;
@@ -1194,10 +1196,11 @@  input_overwrite_node (struct lto_file_decl_data *file_data,
   node->nonfreeing_fn = bp_unpack_value (bp, 1);
   node->merged_comdat = bp_unpack_value (bp, 1);
   node->merged_extern_inline = bp_unpack_value (bp, 1);
-  node->thunk.thunk_p = bp_unpack_value (bp, 1);
+  node->thunk = bp_unpack_value (bp, 1);
   node->parallelized_function = bp_unpack_value (bp, 1);
   node->declare_variant_alt = bp_unpack_value (bp, 1);
   node->calls_declare_variant_alt = bp_unpack_value (bp, 1);
+  *has_thunk_info = bp_unpack_value (bp, 1);
   node->resolution = bp_unpack_enum (bp, ld_plugin_symbol_resolution,
 				     LDPR_NUM_KNOWN);
   node->split_part = bp_unpack_value (bp, 1);
@@ -1235,6 +1238,7 @@  input_node (struct lto_file_decl_data *file_data,
   const char *section;
   order = streamer_read_hwi (ib) + file_data->order_base;
   clone_ref = streamer_read_hwi (ib);
+  bool has_thunk_info;
 
   fn_decl = lto_input_fn_decl_ref (ib, file_data);
 
@@ -1294,7 +1298,7 @@  input_node (struct lto_file_decl_data *file_data,
 
   bp = streamer_read_bitpack (ib);
 
-  input_overwrite_node (file_data, node, tag, &bp);
+  input_overwrite_node (file_data, node, tag, &bp, &has_thunk_info);
 
   /* Store a reference for now, and fix up later to be a pointer.  */
   node->inlined_to = (cgraph_node *) (intptr_t) ref;
@@ -1311,19 +1315,6 @@  input_node (struct lto_file_decl_data *file_data,
   if (section)
     node->set_section_for_node (section);
 
-  if (node->definition)
-    {
-      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.virtual_value = virtual_value;
-      node->thunk.indirect_offset = indirect_offset;
-      node->thunk.this_adjusting = (type & 2);
-      node->thunk.virtual_offset_p = (type & 4);
-    }
   if (node->alias && !node->analyzed && node->weakref)
     node->alias_target = get_alias_symbol (node->decl);
   node->profile_id = streamer_read_hwi (ib);
@@ -1335,6 +1326,9 @@  input_node (struct lto_file_decl_data *file_data,
   if (DECL_STATIC_DESTRUCTOR (node->decl))
     node->set_fini_priority (streamer_read_hwi (ib));
 
+  if (has_thunk_info)
+    thunk_info::get_create (node)->stream_in (ib);
+
   return node;
 }
 
diff --git a/gcc/lto-streamer-in.c b/gcc/lto-streamer-in.c
index a7837c4441e..64037f74ad3 100644
--- a/gcc/lto-streamer-in.c
+++ b/gcc/lto-streamer-in.c
@@ -1232,12 +1232,12 @@  fixup_call_stmt_edges (struct cgraph_node *orig, gimple **stmts)
     orig = orig->clone_of;
   fn = DECL_STRUCT_FUNCTION (orig->decl);
 
-  if (!orig->thunk.thunk_p)
+  if (!orig->thunk)
     fixup_call_stmt_edges_1 (orig, stmts, fn);
   if (orig->clones)
     for (node = orig->clones; node != orig;)
       {
-	if (!node->thunk.thunk_p)
+	if (!node->thunk)
 	  fixup_call_stmt_edges_1 (node, stmts, fn);
 	if (node->clones)
 	  node = node->clones;
diff --git a/gcc/lto/lto-partition.c b/gcc/lto/lto-partition.c
index 8e0488ab13e..7c15181f66f 100644
--- a/gcc/lto/lto-partition.c
+++ b/gcc/lto/lto-partition.c
@@ -181,7 +181,7 @@  add_symbol_to_partition_1 (ltrans_partition part, symtab_node *node)
 
       /* Add all thunks associated with the function.  */
       for (e = cnode->callers; e; e = e->next_caller)
-	if (e->caller->thunk.thunk_p && !e->caller->inlined_to)
+	if (e->caller->thunk && !e->caller->inlined_to)
 	  add_symbol_to_partition_1 (part, e->caller);
     }
 
diff --git a/gcc/symtab-thunks.cc b/gcc/symtab-thunks.cc
new file mode 100644
index 00000000000..1a4aaa2d80b
--- /dev/null
+++ b/gcc/symtab-thunks.cc
@@ -0,0 +1,639 @@ 
+/* Support for thunks in symbol table.
+   Copyright (C) 2003-2020 Free Software Foundation, Inc.
+   Contributed by Jan Hubicka
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "predict.h"
+#include "target.h"
+#include "rtl.h"
+#include "alloc-pool.h"
+#include "cgraph.h"
+#include "symbol-summary.h"
+#include "symtab-thunks.h"
+#include "lto-streamer.h"
+#include "fold-const.h"
+#include "gimple-iterator.h"
+#include "stor-layout.h"
+#include "gimplify-me.h"
+#include "varasm.h"
+#include "output.h"
+#include "cfg.h"
+#include "cfghooks.h"
+#include "gimple-ssa.h"
+#include "gimple-fold.h"
+#include "cfgloop.h"
+#include "tree-into-ssa.h"
+#include "tree-cfg.h"
+#include "cfgcleanup.h"
+#include "tree-pass.h"
+#include "data-streamer.h"
+#include "langhooks.h"
+
+/* Used for vtable lookup in thunk adjusting.  */
+static GTY (()) tree vtable_entry_type;
+
+namespace {
+
+/* Function summary for thunk_infos.  */
+class GTY((user)) thunk_infos_t: public function_summary <thunk_info *>
+{
+public:
+  thunk_infos_t (symbol_table *table, bool ggc):
+    function_summary<thunk_info *> (table, ggc) { }
+
+  /* Hook that is called by summary when a node is duplicated.  */
+  virtual void duplicate (cgraph_node *node,
+			  cgraph_node *node2,
+			  thunk_info *data,
+			  thunk_info *data2);
+};
+
+/* Duplication hook.  */
+void
+thunk_infos_t::duplicate (cgraph_node *, cgraph_node *,
+			  thunk_info *src, thunk_info *dst)
+{
+  *dst = *src;
+}
+
+}  /* anon namespace  */
+
+/* Return thunk_info possibly creating new one.  */
+thunk_info *
+thunk_info::get_create (cgraph_node *node)
+{
+  if (!symtab->m_thunks)
+    {
+      symtab->m_thunks
+	 = new (ggc_alloc_no_dtor <thunk_infos_t> ())
+	     thunk_infos_t (symtab, true);
+      symtab->m_thunks->disable_insertion_hook ();
+    }
+  return symtab->m_thunks->get_create (node);
+}
+
+/* Stream out THIS to OB.  */
+void
+thunk_info::stream_out (lto_simple_output_block *ob)
+{
+  streamer_write_uhwi_stream
+     (ob->main_stream,
+      1 + (this_adjusting != 0) * 2
+      + (virtual_offset_p != 0) * 4);
+  streamer_write_uhwi_stream (ob->main_stream, fixed_offset);
+  streamer_write_uhwi_stream (ob->main_stream, virtual_value);
+  streamer_write_uhwi_stream (ob->main_stream, indirect_offset);
+}
+
+/* Stream in THIS from IB.  */
+void
+thunk_info::stream_in (class lto_input_block *ib)
+{
+  int type = streamer_read_uhwi (ib);
+  fixed_offset = streamer_read_uhwi (ib);
+  virtual_value = streamer_read_uhwi (ib);
+  indirect_offset = streamer_read_uhwi (ib);
+
+  this_adjusting = (type & 2);
+  virtual_offset_p = (type & 4);
+}
+
+/* Dump THIS to F.  */
+void
+thunk_info::dump (FILE *f)
+{
+  if (alias)
+    fprintf (f, "  of %s (asm:%s)",
+	     lang_hooks.decl_printable_name (alias, 2),
+	     IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (alias)));
+  fprintf (f, " fixed offset %i virtual value %i indirect_offset %i "
+	      "has virtual offset %i\n",
+	   (int)fixed_offset,
+	   (int)virtual_value,
+	   (int)indirect_offset,
+	   (int)virtual_offset_p);
+}
+
+/* Hash THIS.  */
+hashval_t
+thunk_info::hash ()
+{
+  inchash::hash hstate;
+  hstate.add_hwi (fixed_offset);
+  hstate.add_hwi (virtual_value);
+  hstate.add_flag (this_adjusting);
+  hstate.add_flag (virtual_offset_p);
+  return hstate.end ();
+}
+
+/* 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 indirect_offset)
+{
+  gassign *stmt;
+  tree ret;
+
+  if (this_adjusting
+      && fixed_offset != 0)
+    {
+      stmt = gimple_build_assign
+		(ptr, fold_build_pointer_plus_hwi_loc (input_location,
+						       ptr,
+						       fixed_offset));
+      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)
+    {
+      tree vtabletmp;
+      tree vtabletmp2;
+      tree vtabletmp3;
+
+      vtabletmp = create_tmp_reg
+		    (build_pointer_type
+			  (build_pointer_type (vtable_entry_type)), "vptr");
+
+      /* The vptr is always at offset zero in the object.  */
+      stmt = gimple_build_assign (vtabletmp,
+				  build1 (NOP_EXPR, TREE_TYPE (vtabletmp),
+					  ptr));
+      gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+
+      /* Form the vtable address.  */
+      vtabletmp2 = create_tmp_reg (TREE_TYPE (TREE_TYPE (vtabletmp)),
+				     "vtableaddr");
+      stmt = gimple_build_assign (vtabletmp2,
+				  build_simple_mem_ref (vtabletmp));
+      gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+
+      /* Find the entry with the vcall offset.  */
+      stmt = gimple_build_assign (vtabletmp2,
+				  fold_build_pointer_plus_loc (input_location,
+							       vtabletmp2,
+							       virtual_offset));
+      gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+
+      /* Get the offset itself.  */
+      vtabletmp3 = create_tmp_reg (TREE_TYPE (TREE_TYPE (vtabletmp2)),
+				     "vcalloffset");
+      stmt = gimple_build_assign (vtabletmp3,
+				  build_simple_mem_ref (vtabletmp2));
+      gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+
+      /* Adjust the `this' pointer.  */
+      ptr = fold_build_pointer_plus_loc (input_location, ptr, vtabletmp3);
+      ptr = force_gimple_operand_gsi (bsi, ptr, true, NULL_TREE, false,
+				      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.  */
+    {
+      tree ptrtmp;
+
+      if (VAR_P (ptr))
+	ptrtmp = ptr;
+      else
+	{
+	  ptrtmp = create_tmp_reg (TREE_TYPE (ptr), "ptr");
+	  stmt = gimple_build_assign (ptrtmp, ptr);
+	  gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+	}
+      ptr = fold_build_pointer_plus_hwi_loc (input_location,
+					     ptrtmp, fixed_offset);
+    }
+
+  /* Emit the statement and gimplify the adjustment expression.  */
+  ret = create_tmp_reg (TREE_TYPE (ptr), "adjusted_this");
+  stmt = gimple_build_assign (ret, ptr);
+  gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+
+  return ret;
+}
+
+/* Expand thunk NODE to gimple if possible.
+   When FORCE_GIMPLE_THUNK is true, gimple thunk is created and
+   no assembler is produced.
+   When OUTPUT_ASM_THUNK is true, also produce assembler for
+   thunks that are not lowered.  */
+bool
+expand_thunk (cgraph_node *node, bool output_asm_thunks,
+	      bool force_gimple_thunk)
+{
+  thunk_info *info = thunk_info::get (node);
+  bool this_adjusting = info->this_adjusting;
+  HOST_WIDE_INT fixed_offset = info->fixed_offset;
+  HOST_WIDE_INT virtual_value = info->virtual_value;
+  HOST_WIDE_INT indirect_offset = info->indirect_offset;
+  tree virtual_offset = NULL;
+  tree alias = node->callees->callee->decl;
+  tree thunk_fndecl = node->decl;
+  tree a;
+
+  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))
+    {
+      tree fn_block;
+      tree restype = TREE_TYPE (TREE_TYPE (thunk_fndecl));
+
+      if (!output_asm_thunks)
+	{
+	  node->analyzed = true;
+	  return false;
+	}
+
+      if (in_lto_p)
+	node->get_untransformed_body ();
+      a = DECL_ARGUMENTS (thunk_fndecl);
+
+      current_function_decl = thunk_fndecl;
+
+      /* Ensure thunks are emitted in their correct sections.  */
+      resolve_unique_section (thunk_fndecl, 0,
+			      flag_function_sections);
+
+      DECL_RESULT (thunk_fndecl)
+	= build_decl (DECL_SOURCE_LOCATION (thunk_fndecl),
+		      RESULT_DECL, 0, restype);
+      DECL_CONTEXT (DECL_RESULT (thunk_fndecl)) = thunk_fndecl;
+
+      /* The back end expects DECL_INITIAL to contain a BLOCK, so we
+	 create one.  */
+      fn_block = make_node (BLOCK);
+      BLOCK_VARS (fn_block) = a;
+      DECL_INITIAL (thunk_fndecl) = fn_block;
+      BLOCK_SUPERCONTEXT (fn_block) = thunk_fndecl;
+      allocate_struct_function (thunk_fndecl, false);
+      init_function_start (thunk_fndecl);
+      cfun->is_thunk = 1;
+      insn_locations_init ();
+      set_curr_insn_location (DECL_SOURCE_LOCATION (thunk_fndecl));
+      prologue_location = curr_insn_location ();
+
+      targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl,
+				       fixed_offset, virtual_value, alias);
+
+      insn_locations_finalize ();
+      init_insn_lengths ();
+      free_after_compilation (cfun);
+      TREE_ASM_WRITTEN (thunk_fndecl) = 1;
+      node->thunk = false;
+      node->analyzed = false;
+    }
+  else if (stdarg_p (TREE_TYPE (thunk_fndecl)))
+    {
+      error ("generic thunk code fails for method %qD which uses %<...%>",
+	     thunk_fndecl);
+      TREE_ASM_WRITTEN (thunk_fndecl) = 1;
+      node->analyzed = true;
+      return false;
+    }
+  else
+    {
+      tree restype;
+      basic_block bb, then_bb, else_bb, return_bb;
+      gimple_stmt_iterator bsi;
+      int nargs = 0;
+      tree arg;
+      int i;
+      tree resdecl;
+      tree restmp = NULL;
+
+      gcall *call;
+      greturn *ret;
+      bool alias_is_noreturn = TREE_THIS_VOLATILE (alias);
+
+      /* We may be called from expand_thunk that releases body except for
+	 DECL_ARGUMENTS.  In this case force_gimple_thunk is true.  */
+      if (in_lto_p && !force_gimple_thunk)
+	node->get_untransformed_body ();
+
+      /* We need to force DECL_IGNORED_P when the thunk is created
+	 after early debug was run.  */
+      if (force_gimple_thunk)
+	DECL_IGNORED_P (thunk_fndecl) = 1;
+
+      a = DECL_ARGUMENTS (thunk_fndecl);
+
+      current_function_decl = thunk_fndecl;
+
+      /* Ensure thunks are emitted in their correct sections.  */
+      resolve_unique_section (thunk_fndecl, 0,
+			      flag_function_sections);
+
+      bitmap_obstack_initialize (NULL);
+
+      if (info->virtual_offset_p)
+	virtual_offset = size_int (virtual_value);
+
+      /* Build the return declaration for the function.  */
+      restype = TREE_TYPE (TREE_TYPE (thunk_fndecl));
+      if (DECL_RESULT (thunk_fndecl) == NULL_TREE)
+	{
+	  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;
+	}
+      else
+	resdecl = DECL_RESULT (thunk_fndecl);
+
+      profile_count cfg_count = node->count;
+      if (!cfg_count.initialized_p ())
+	cfg_count = profile_count::from_gcov_type
+			 (BB_FREQ_MAX).guessed_local ();
+
+      bb = then_bb = else_bb = return_bb
+	= init_lowered_empty_function (thunk_fndecl, true, cfg_count);
+
+      bsi = gsi_start_bb (bb);
+
+      /* Build call to the function being thunked.  */
+      if (!VOID_TYPE_P (restype)
+	  && (!alias_is_noreturn
+	      || TREE_ADDRESSABLE (restype)
+	      || TREE_CODE (TYPE_SIZE_UNIT (restype)) != INTEGER_CST))
+	{
+	  if (DECL_BY_REFERENCE (resdecl))
+	    {
+	      restmp = gimple_fold_indirect_ref (resdecl);
+	      if (!restmp)
+		restmp = build2 (MEM_REF,
+				 TREE_TYPE (TREE_TYPE (resdecl)),
+				 resdecl,
+				 build_int_cst (TREE_TYPE (resdecl), 0));
+	    }
+	  else if (!is_gimple_reg_type (restype))
+	    {
+	      if (aggregate_value_p (resdecl, TREE_TYPE (thunk_fndecl)))
+		{
+		  restmp = resdecl;
+
+		  if (VAR_P (restmp))
+		    {
+		      add_local_decl (cfun, restmp);
+		      BLOCK_VARS (DECL_INITIAL (current_function_decl))
+			= restmp;
+		    }
+		}
+	      else
+		restmp = create_tmp_var (restype, "retval");
+	    }
+	  else
+	    restmp = create_tmp_reg (restype, "retval");
+	}
+
+      for (arg = a; arg; arg = DECL_CHAIN (arg))
+	nargs++;
+      auto_vec<tree> vargs (nargs);
+      i = 0;
+      arg = a;
+      if (this_adjusting)
+	{
+	  vargs.quick_push (thunk_adjust (&bsi, a, 1, fixed_offset,
+					  virtual_offset, indirect_offset));
+	  arg = DECL_CHAIN (a);
+	  i = 1;
+	}
+
+      if (nargs)
+	for (; i < nargs; i++, arg = DECL_CHAIN (arg))
+	  {
+	    tree tmp = arg;
+	    DECL_NOT_GIMPLE_REG_P (arg) = 0;
+	    if (!is_gimple_val (arg))
+	      {
+		tmp = create_tmp_reg (TYPE_MAIN_VARIANT
+				      (TREE_TYPE (arg)), "arg");
+		gimple *stmt = gimple_build_assign (tmp, arg);
+		gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
+	      }
+	    vargs.quick_push (tmp);
+	  }
+      call = gimple_build_call_vec (build_fold_addr_expr_loc (0, alias), vargs);
+      node->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 required to
+	 return values with DECL_BY_REFERENCE.  */
+      if (aggregate_value_p (resdecl, TREE_TYPE (thunk_fndecl))
+	  && (!is_gimple_reg_type (TREE_TYPE (resdecl))
+	      || DECL_BY_REFERENCE (resdecl)))
+	gimple_call_set_return_slot_opt (call, true);
+
+      if (restmp)
+	{
+	  gimple_call_set_lhs (call, restmp);
+	  gcc_assert (useless_type_conversion_p (TREE_TYPE (restmp),
+						 TREE_TYPE (TREE_TYPE (alias))));
+	}
+      gsi_insert_after (&bsi, call, GSI_NEW_STMT);
+      if (!alias_is_noreturn)
+	{
+	  if (restmp && !this_adjusting
+	      && (fixed_offset || virtual_offset))
+	    {
+	      tree true_label = NULL_TREE;
+
+	      if (TREE_CODE (TREE_TYPE (restmp)) == POINTER_TYPE)
+		{
+		  gimple *stmt;
+		  edge e;
+		  /* If the return type is a pointer, we need to
+		     protect against NULL.  We know there will be an
+		     adjustment, because that's why we're emitting a
+		     thunk.  */
+		  then_bb = create_basic_block (NULL, bb);
+		  then_bb->count = cfg_count - cfg_count.apply_scale (1, 16);
+		  return_bb = create_basic_block (NULL, then_bb);
+		  return_bb->count = cfg_count;
+		  else_bb = create_basic_block (NULL, else_bb);
+		  else_bb->count = cfg_count.apply_scale (1, 16);
+		  add_bb_to_loop (then_bb, bb->loop_father);
+		  add_bb_to_loop (return_bb, bb->loop_father);
+		  add_bb_to_loop (else_bb, bb->loop_father);
+		  remove_edge (single_succ_edge (bb));
+		  true_label = gimple_block_label (then_bb);
+		  stmt = gimple_build_cond (NE_EXPR, restmp,
+					    build_zero_cst (TREE_TYPE (restmp)),
+					    NULL_TREE, NULL_TREE);
+		  gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
+		  e = make_edge (bb, then_bb, EDGE_TRUE_VALUE);
+		  e->probability = profile_probability::guessed_always ()
+					.apply_scale (1, 16);
+		  e = make_edge (bb, else_bb, EDGE_FALSE_VALUE);
+		  e->probability = profile_probability::guessed_always ()
+					.apply_scale (1, 16);
+		  make_single_succ_edge (return_bb,
+					 EXIT_BLOCK_PTR_FOR_FN (cfun), 0);
+		  make_single_succ_edge (then_bb, return_bb, EDGE_FALLTHRU);
+		  e = make_edge (else_bb, return_bb, EDGE_FALLTHRU);
+		  e->probability = profile_probability::always ();
+		  bsi = gsi_last_bb (then_bb);
+		}
+
+	      restmp = thunk_adjust (&bsi, restmp, /*this_adjusting=*/0,
+				     fixed_offset, virtual_offset,
+				     indirect_offset);
+	      if (true_label)
+		{
+		  gimple *stmt;
+		  bsi = gsi_last_bb (else_bb);
+		  stmt = gimple_build_assign (restmp,
+					      build_zero_cst
+						 (TREE_TYPE (restmp)));
+		  gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
+		  bsi = gsi_last_bb (return_bb);
+		}
+	    }
+	  else
+	    {
+	      gimple_call_set_tail (call, true);
+	      cfun->tail_call_marked = true;
+	    }
+
+	  /* Build return value.  */
+	  if (!DECL_BY_REFERENCE (resdecl))
+	    ret = gimple_build_return (restmp);
+	  else
+	    ret = gimple_build_return (resdecl);
+
+	  gsi_insert_after (&bsi, ret, GSI_NEW_STMT);
+	}
+      else
+	{
+	  gimple_call_set_tail (call, true);
+	  cfun->tail_call_marked = true;
+	  remove_edge (single_succ_edge (bb));
+	}
+
+      cfun->gimple_df->in_ssa_p = true;
+      update_max_bb_count ();
+      profile_status_for_fn (cfun)
+	= cfg_count.initialized_p () && cfg_count.ipa_p ()
+	  ? PROFILE_READ : PROFILE_GUESSED;
+      /* FIXME: C++ FE should stop setting TREE_ASM_WRITTEN on thunks.  */
+      TREE_ASM_WRITTEN (thunk_fndecl) = false;
+      delete_unreachable_blocks ();
+      update_ssa (TODO_update_ssa);
+      checking_verify_flow_info ();
+      free_dominance_info (CDI_DOMINATORS);
+
+      /* Since we want to emit the thunk, we explicitly mark its name as
+	 referenced.  */
+      node->thunk = false;
+      node->lowered = true;
+      bitmap_obstack_release (NULL);
+    }
+  current_function_decl = NULL;
+  set_cfun (NULL);
+  return true;
+}
+
+void
+symtab_thunks_cc_finalize (void)
+{
+  vtable_entry_type = NULL;
+}
+
+#include "gt-symtab-thunks.h"
diff --git a/gcc/symtab-thunks.h b/gcc/symtab-thunks.h
new file mode 100644
index 00000000000..a23fc552950
--- /dev/null
+++ b/gcc/symtab-thunks.h
@@ -0,0 +1,166 @@ 
+/* Representation of thunks inside symbol table.
+   Copyright (C) 2003-2020 Free Software Foundation, Inc.
+   Contributed by Jan Hubicka
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_SYMTAB_THUNKS_H
+#define GCC_SYMTAB_THUNKS_H
+
+/* This symbol annotation holds information about thunk.
+
+   Thunks are basically wrappers around methods which are introduced in case
+   of multiple inheritance in order to adjust the value of the "this" pointer
+   or of the returned value.
+
+   In the case of this-adjusting thunks, each back-end can override the
+   can_output_mi_thunk/output_mi_thunk target hooks to generate a minimal thunk
+   (with a tail call for instance) directly as assembly.  For the default hook
+   or for the case where the can_output_mi_thunk hooks return false, the thunk
+   is gimplified and lowered using the regular machinery.  */
+
+struct GTY(()) thunk_info {
+  /* Constructor.  */
+  thunk_info ()
+    : fixed_offset (0),
+      virtual_value (0),
+      indirect_offset (0),
+      alias (NULL),
+      this_adjusting (false),
+      virtual_offset_p (false)
+  {
+  }
+  /* Copy constructor.  */
+  thunk_info (const thunk_info &t)
+    : fixed_offset (t.fixed_offset),
+      virtual_value (t.virtual_value),
+      indirect_offset (t.indirect_offset),
+      alias (t.alias),
+      this_adjusting (t.this_adjusting),
+      virtual_offset_p (t.virtual_offset_p)
+  {
+  }
+
+  /* Compare for equiality.  */
+  bool
+  operator==(const thunk_info &other) const
+  {
+    return fixed_offset == other.fixed_offset
+	   && virtual_value == other.virtual_value
+	   && indirect_offset == other.indirect_offset
+	   && this_adjusting == other.this_adjusting
+	   && virtual_offset_p == other.virtual_offset_p;
+  }
+  bool
+  operator!=(const thunk_info &other) const
+  {
+    return !(*this == other);
+  }
+  /* Copy operator.  */
+  thunk_info &
+  operator=(const thunk_info &other)
+  {
+    fixed_offset = other.fixed_offset;
+    virtual_value = other.virtual_value;
+    indirect_offset = other.indirect_offset;
+    this_adjusting = other.this_adjusting;
+    virtual_offset_p = other.virtual_offset_p;
+    return *this;
+  }
+
+  /* Offset used to adjust "this".  */
+  HOST_WIDE_INT fixed_offset;
+
+  /* Offset in the virtual table to get the offset to adjust "this".  Valid iff
+     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;
+
+  /* Nonzero for a "this" adjusting thunk and zero for a result adjusting
+     thunk.  */
+  bool this_adjusting;
+
+  /* If true, this thunk is what we call a virtual thunk.  In this case:
+     * for this-adjusting thunks, after the FIXED_OFFSET based adjustment is
+       done, add to the result the offset found in the vtable at:
+	 vptr + VIRTUAL_VALUE
+     * for result-adjusting thunks, the FIXED_OFFSET adjustment is done after
+       the virtual one.  */
+  bool virtual_offset_p;
+
+
+
+  /* Dump thunk_info.  */
+  void dump (FILE *);
+
+  /* Stream out thunk_info.  */
+  void stream_out (class lto_simple_output_block *);
+
+  /* Stream in trunk_info.  */
+  void stream_in (class lto_input_block *);
+
+  hashval_t hash ();
+
+
+
+  /* Return thunk_info, if available.  */
+  static thunk_info *get (cgraph_node *node);
+
+  /* Return thunk_info possibly creating new one.  */
+  static thunk_info *get_create (cgraph_node *node);
+
+  /* Remove thunk_info.  */
+  static void remove (cgraph_node *node);
+
+  /* Release all thunk_infos.  */
+  static void release (void);
+};
+
+bool expand_thunk (cgraph_node *, bool, bool);
+
+/* Return thunk_info, if available.  */
+inline thunk_info *
+thunk_info::get (cgraph_node *node)
+{
+  if (!symtab->m_thunks)
+    return NULL;
+  return symtab->m_thunks->get (node);
+}
+
+/* Remove thunk_info association for NODE.  */
+inline void
+thunk_info::remove (cgraph_node *node)
+{
+  symtab->m_thunks->remove (node);
+}
+
+/* Free thunk info summaries.  */
+inline void
+thunk_info::release ()
+{
+  if (symtab->m_thunks)
+    delete (symtab->m_thunks);
+  symtab->m_thunks = NULL;
+}
+#endif  /* GCC_SYMTAB_THUNKS_H  */
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 8c1e1e1f44f..20e231f4d2a 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -2507,6 +2507,7 @@  toplev::finalize (void)
 
   cgraph_c_finalize ();
   cgraphunit_c_finalize ();
+  symtab_thunks_cc_finalize ();
   dwarf2out_c_finalize ();
   gcse_c_finalize ();
   ipa_cp_c_finalize ();
diff --git a/gcc/trans-mem.c b/gcc/trans-mem.c
index e466b24400d..e4f264d6ae0 100644
--- a/gcc/trans-mem.c
+++ b/gcc/trans-mem.c
@@ -51,6 +51,9 @@ 
 #include "tree-ssa-address.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "alloc-pool.h"
+#include "symbol-summary.h"
+#include "symtab-thunks.h"
 
 #define A_RUNINSTRUMENTEDCODE	0x0001
 #define A_RUNUNINSTRUMENTEDCODE	0x0002
@@ -4729,7 +4732,8 @@  ipa_tm_mayenterirr_function (struct cgraph_node *node)
      result in one of the bits above being set so that we will not
      have to recurse next time.  */
   if (node->alias)
-    return ipa_tm_mayenterirr_function (cgraph_node::get (node->thunk.alias));
+    return ipa_tm_mayenterirr_function
+		 (cgraph_node::get (thunk_info::get (node)->alias));
 
   /* What remains is unmarked local functions without items that force
      the function to go irrevocable.  */
@@ -5475,7 +5479,7 @@  ipa_tm_execute (void)
 		 we need not scan the callees now, as the base will do.  */
 	      if (node->alias)
 		{
-		  node = cgraph_node::get (node->thunk.alias);
+		  node = cgraph_node::get (thunk_info::get (node)->alias);
 		  d = get_cg_data (&node, true);
 		  maybe_push_queue (node, &tm_callees, &d->in_callee_queue);
 		  continue;
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 0d33ee72c0f..32fb3789f40 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -61,6 +61,9 @@  along with GCC; see the file COPYING3.  If not see
 #include "sreal.h"
 #include "tree-cfgcleanup.h"
 #include "tree-ssa-live.h"
+#include "alloc-pool.h"
+#include "symbol-summary.h"
+#include "symtab-thunks.h"
 
 /* I'm not real happy about this, but we need to handle gimple and
    non-gimple trees.  */
@@ -4792,13 +4795,14 @@  expand_call_inline (basic_block bb, gimple *stmt, copy_body_data *id,
 
   /* If callee is thunk, all we need is to adjust the THIS pointer
      and redirect to function being thunked.  */
-  if (id->src_node->thunk.thunk_p)
+  if (id->src_node->thunk)
     {
       cgraph_edge *edge;
       tree virtual_offset = NULL;
       profile_count count = cg_edge->count;
       tree op;
       gimple_stmt_iterator iter = gsi_for_stmt (stmt);
+      thunk_info *info = thunk_info::get (id->src_node);
 
       cgraph_edge::remove (cg_edge);
       edge = id->src_node->callees->clone (id->dst_node, call_stmt,
@@ -4807,16 +4811,16 @@  expand_call_inline (basic_block bb, gimple *stmt, copy_body_data *id,
 					   profile_count::one (),
 				           true);
       edge->count = count;
-      if (id->src_node->thunk.virtual_offset_p)
-        virtual_offset = size_int (id->src_node->thunk.virtual_value);
+      if (info->virtual_offset_p)
+	virtual_offset = size_int (info->virtual_value);
       op = create_tmp_reg_fn (cfun, TREE_TYPE (gimple_call_arg (stmt, 0)),
 			      NULL);
       gsi_insert_before (&iter, gimple_build_assign (op,
 						    gimple_call_arg (stmt, 0)),
 			 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, id->src_node->thunk.indirect_offset);
+      gcc_assert (info->this_adjusting);
+      op = thunk_adjust (&iter, op, 1, info->fixed_offset,
+			 virtual_offset, info->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 433f37fffe3..788883faa4c 100644
--- a/gcc/tree-nested.c
+++ b/gcc/tree-nested.c
@@ -45,6 +45,7 @@ 
 #include "alloc-pool.h"
 #include "tree-nested.h"
 #include "symbol-summary.h"
+#include "symtab-thunks.h"
 
 /* Summary of nested functions.  */
 static function_summary <nested_function_info *>
@@ -937,7 +938,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;
+  info->thunk_p = cgn->thunk;
 
   for (cgn = first_nested_function (cgn); cgn;
        cgn = next_nested_function (cgn))
@@ -3047,7 +3048,7 @@  convert_all_function_calls (struct nesting_info *root)
     if (n->thunk_p)
       {
 	tree decl = n->context;
-	tree alias = cgraph_node::get (decl)->thunk.alias;
+	tree alias = thunk_info::get (cgraph_node::get (decl))->alias;
 	DECL_STATIC_CHAIN (decl) = DECL_STATIC_CHAIN (alias);
       }
 
@@ -3083,7 +3084,7 @@  convert_all_function_calls (struct nesting_info *root)
 	if (n->thunk_p)
 	  {
 	    tree decl = n->context;
-	    tree alias = cgraph_node::get (decl)->thunk.alias;
+	    tree alias = thunk_info::get (cgraph_node::get (decl))->alias;
 	    DECL_STATIC_CHAIN (decl) = DECL_STATIC_CHAIN (alias);
 	  }
     }
@@ -3638,7 +3639,7 @@  gimplify_all_functions (struct cgraph_node *root)
     gimplify_function_tree (root->decl);
   for (iter = first_nested_function (root); iter;
        iter = next_nested_function (iter))
-    if (!iter->thunk.thunk_p)
+    if (!iter->thunk)
       gimplify_all_functions (iter);
 }
 
diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c
index 6c0838261a1..07a6d995082 100644
--- a/gcc/tree-profile.c
+++ b/gcc/tree-profile.c
@@ -55,6 +55,9 @@  along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "stor-layout.h"
 #include "xregex.h"
+#include "alloc-pool.h"
+#include "symbol-summary.h"
+#include "symtab-thunks.h"
 
 static GTY(()) tree gcov_type_node;
 static GTY(()) tree tree_interval_profiler_fn;
@@ -726,7 +729,7 @@  tree_profiling (void)
   FOR_EACH_DEFINED_FUNCTION (node)
     {
       bool thunk = false;
-      if (!gimple_has_body_p (node->decl) && !node->thunk.thunk_p)
+      if (!gimple_has_body_p (node->decl) && !node->thunk)
 	continue;
 
       /* Don't profile functions produced for builtin stuff.  */
@@ -747,7 +750,7 @@  tree_profiling (void)
       if (!include_source_file_for_profile (file))
 	continue;
 
-      if (node->thunk.thunk_p)
+      if (node->thunk)
 	{
 	  /* We cannot expand variadic thunks to Gimple.  */
 	  if (stdarg_p (TREE_TYPE (node->decl)))
@@ -756,7 +759,7 @@  tree_profiling (void)
 	  /* When generate profile, expand thunk to gimple so it can be
 	     instrumented same way as other functions.  */
 	  if (profile_arc_flag)
-	    node->expand_thunk (false, true);
+	    expand_thunk (node, false, true);
 	  /* Read cgraph profile but keep function as thunk at profile-use
 	     time.  */
 	  else
diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c
index ac29365e809..9bac06f97af 100644
--- a/gcc/tree-ssa-structalias.c
+++ b/gcc/tree-ssa-structalias.c
@@ -7972,7 +7972,7 @@  static bool
 associate_varinfo_to_alias (struct cgraph_node *node, void *data)
 {
   if ((node->alias
-       || (node->thunk.thunk_p
+       || (node->thunk
 	   && ! node->inlined_to))
       && node->analyzed
       && !node->ifunc_resolver)
diff --git a/gcc/tree.c b/gcc/tree.c
index 555ba97e68b..fa03d963eed 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -5777,7 +5777,7 @@  free_lang_data_in_decl (tree decl, class free_lang_data_d *fld)
 	      DECL_INITIAL (decl) = error_mark_node;
 	    }
 	}
-      if (gimple_has_body_p (decl) || (node && node->thunk.thunk_p))
+      if (gimple_has_body_p (decl) || (node && node->thunk))
 	{
 	  tree t;
 
diff --git a/gcc/value-prof.c b/gcc/value-prof.c
index 95d33c63a0c..db4fb8a2126 100644
--- a/gcc/value-prof.c
+++ b/gcc/value-prof.c
@@ -1222,7 +1222,7 @@  init_node_map (bool local)
   cgraph_node_map = new hash_map<profile_id_hash, cgraph_node *>;
 
   FOR_EACH_DEFINED_FUNCTION (n)
-    if (n->has_gimple_body_p () || n->thunk.thunk_p)
+    if (n->has_gimple_body_p () || n->thunk)
       {
 	cgraph_node **val;
 	dump_user_location_t loc
diff --git a/include/ansidecl.h b/include/ansidecl.h
index ec7a13f016a..e5859b8d1e0 100644
--- a/include/ansidecl.h
+++ b/include/ansidecl.h
@@ -190,6 +190,15 @@  So instead we use the macro below and test it against specific values.  */
 # endif /* GNUC >= 3.0 */
 #endif /* ATTRIBUTE_PURE */
 
+/* Attribute `pure' was already valid as of gcc 3.0 (and earlier).  */
+#ifndef ATTRIBUTE_CONST
+# if (GCC_VERSION >= 3000)
+#  define ATTRIBUTE_CONST __attribute__ ((__const__))
+# else
+#  define ATTRIBUTE_CONST
+# endif /* GNUC >= 3.0 */
+#endif /* ATTRIBUTE_CONST */
+
 /* Use ATTRIBUTE_PRINTF when the format specifier must not be NULL.
    This was the case for the `printf' format attribute by itself
    before GCC 3.3, but as of 3.3 we need to add the `nonnull'