diff mbox series

Move nested function info out of cgraph_node

Message ID 20201022002326.GD41258@kam.mff.cuni.cz
State New
Headers show
Series Move nested function info out of cgraph_node | expand

Commit Message

Jan Hubicka Oct. 22, 2020, 12:23 a.m. UTC
Hi,
this patch moves nested function information out of cgraph_node (to a summary).
This saves memory (especially at WPA time) and also makes nested function
support more contained.

Bootstrapped/regtested x86_64-linux.  Plan to commit it tomorrow.

gcc/ChangeLog:

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

	* cgraph.c: Include tree-nested.h
	(cgraph_node::create): Call maybe_record_nested_function.
	(cgraph_node::remove): Do not remove function from nested function
	infos.
	(cgraph_node::dump): Update.
	(cgraph_node::unnest): Move to tree-nested.c
	(cgraph_node::verify_node): Update.
	(cgraph_c_finalize): Call nested_function_info::release.
	* cgraph.h (struct symtab_node): Remove nested function info.
	* cgraphclones.c (cgraph_node::create_clone): Do not clone nested
	function info.
	* cgraphunit.c (cgraph_node::analyze): Update.
	(cgraph_node::expand): Do not worry about nested functions; they are
	lowered.
	(symbol_table::finalize_compilation_unit): Call
	nested_function_info::release.
	* gimplify.c: Include tree-nested.h
	(unshare_body): Update.
	(unvisit_body): Update.
	* omp-offload.c (omp_discover_implicit_declare_target): Update.
	* tree-nested.c: Include alloc-pool.h, tree-nested.h, symbol-summary.h
	(nested_function_sum): New static variable.
	(nested_function_info::get): New member function.
	(nested_function_info::get_create): New member function.
	(unnest_function): New function.
	(nested_function_info::~nested_function_info): New member function.
	(nested_function_info::release): New function.
	(maybe_record_nested_function): New function.
	(lookup_element_for_decl): Update.
	(check_for_nested_with_variably_modified): Update.
	(create_nesting_tree): Update.
	(unnest_nesting_tree_1): Update.
	(gimplify_all_functions): Update.
	(lower_nested_functions): Update.
	* tree-nested.h (class nested_function_info): New class.
	(maybe_record_nested_function): Declare.
	(unnest_function): Declare.
	(first_nested_function): New inline function.
	(next_nested_function): New inline function.
	(nested_function_origin): New inline function.

gcc/ada/ChangeLog:

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

	* gcc-interface/trans.c: Include tree-nested.h
	(walk_nesting_tree): Update for new nested function info.

gcc/c-family/ChangeLog:

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

	* c-gimplify.c: Include tree-nested.h
	(c_genericize): Update for new nested function info.

gcc/d/ChangeLog:

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

	* decl.cc: Include tree-nested.h
	(get_symbol_decl): Update for new nested function info.
diff mbox series

Patch

diff --git a/gcc/ada/gcc-interface/trans.c b/gcc/ada/gcc-interface/trans.c
index f03d591a323..6babbd41d52 100644
--- a/gcc/ada/gcc-interface/trans.c
+++ b/gcc/ada/gcc-interface/trans.c
@@ -50,6 +50,7 @@ 
 #include "gomp-constants.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "tree-nested.h"
 
 #include "ada.h"
 #include "adadecode.h"
@@ -3696,7 +3697,8 @@  finalize_nrv_unc_r (tree *tp, int *walk_subtrees, void *data)
 static void
 walk_nesting_tree (struct cgraph_node *node, walk_tree_fn func, void *data)
 {
-  for (node = node->nested; node; node = node->next_nested)
+  for (node = first_nested_function (node);
+       node; node = next_nested_function (node))
     {
       walk_tree_without_duplicates (&DECL_SAVED_TREE (node->decl), func, data);
       walk_nesting_tree (node, func, data);
diff --git a/gcc/c-family/c-gimplify.c b/gcc/c-family/c-gimplify.c
index d1e391590dd..a7c0ec3be0d 100644
--- a/gcc/c-family/c-gimplify.c
+++ b/gcc/c-family/c-gimplify.c
@@ -39,6 +39,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "dumpfile.h"
 #include "c-ubsan.h"
+#include "tree-nested.h"
 
 /*  The gimplification pass converts the language-dependent trees
     (ld-trees) emitted by the parser into language-independent trees
@@ -572,7 +573,8 @@  c_genericize (tree fndecl)
 
   /* Dump all nested functions now.  */
   cgn = cgraph_node::get_create (fndecl);
-  for (cgn = cgn->nested; cgn ; cgn = cgn->next_nested)
+  for (cgn = first_nested_function (cgn);
+       cgn; cgn = next_nested_function (cgn))
     c_genericize (cgn->decl);
 }
 
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index f018020fa4b..9480935ff84 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -64,6 +64,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "selftest.h"
 #include "tree-into-ssa.h"
 #include "ipa-inline.h"
+#include "tree-nested.h"
 
 /* FIXME: Only for PROP_loops, but cgraph shouldn't have to know about this.  */
 #include "tree-pass.h"
@@ -517,13 +518,8 @@  cgraph_node::create (tree decl)
     node->ifunc_resolver = true;
 
   node->register_symbol ();
+  maybe_record_nested_function (node);
 
-  if (DECL_CONTEXT (decl) && TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL)
-    {
-      node->origin = cgraph_node::get_create (DECL_CONTEXT (decl));
-      node->next_nested = node->origin->nested;
-      node->origin->nested = node;
-    }
   return node;
 }
 
@@ -1861,22 +1857,7 @@  cgraph_node::remove (void)
      */
   force_output = false;
   forced_by_abi = false;
-  cgraph_node *next;
-  for (cgraph_node *n = nested; n; n = next)
-  {
-    next = n->next_nested;
-    n->origin = NULL;
-    n->next_nested = NULL;
-  }
-  nested = NULL;
-  if (origin)
-    {
-      cgraph_node **node2 = &origin->nested;
 
-      while (*node2 != this)
-	node2 = &(*node2)->next_nested;
-      *node2 = next_nested;
-    }
   unregister ();
   if (prev_sibling_clone)
     prev_sibling_clone->next_sibling_clone = next_sibling_clone;
@@ -2139,7 +2120,7 @@  cgraph_node::dump (FILE *f)
     }
   if (tp_first_run > 0)
     fprintf (f, " first_run:%" PRId64, (int64_t) tp_first_run);
-  if (origin)
+  if (cgraph_node *origin = nested_function_origin (this))
     fprintf (f, " nested in:%s", origin->dump_asm_name ());
   if (gimple_has_body_p (decl))
     fprintf (f, " body");
@@ -2348,19 +2329,6 @@  cgraph_function_possibly_inlined_p (tree decl)
   return DECL_POSSIBLY_INLINED (decl);
 }
 
-/* cgraph_node is no longer nested function; update cgraph accordingly.  */
-void
-cgraph_node::unnest (void)
-{
-  cgraph_node **node2 = &origin->nested;
-  gcc_assert (origin);
-
-  while (*node2 != this)
-    node2 = &(*node2)->next_nested;
-  *node2 = next_nested;
-  origin = NULL;
-}
-
 /* Return function availability.  See cgraph.h for description of individual
    return values.  */
 enum availability
@@ -3798,27 +3766,32 @@  cgraph_node::verify_node (void)
 	}
     }
 
-  if (nested != NULL)
+  if (nested_function_info *info = nested_function_info::get (this))
     {
-      for (cgraph_node *n = nested; n != NULL; n = n->next_nested)
+      if (info->nested != NULL)
 	{
-	  if (n->origin == NULL)
-	    {
-	      error ("missing origin for a node in a nested list");
-	      error_found = true;
-	    }
-	  else if (n->origin != this)
+	  for (cgraph_node *n = info->nested; n != NULL;
+	       n = next_nested_function (n))
 	    {
-	      error ("origin points to a different parent");
-	      error_found = true;
-	      break;
+	      nested_function_info *ninfo = nested_function_info::get (n);
+	      if (ninfo->origin == NULL)
+		{
+		  error ("missing origin for a node in a nested list");
+		  error_found = true;
+		}
+	      else if (ninfo->origin != this)
+		{
+		  error ("origin points to a different parent");
+		  error_found = true;
+		  break;
+		}
 	    }
 	}
-    }
-  if (next_nested != NULL && origin == NULL)
-    {
-      error ("missing origin for a node in a nested list");
-      error_found = true;
+      if (info->next_nested != NULL && info->origin == NULL)
+	{
+	  error ("missing origin for a node in a nested list");
+	  error_found = true;
+	}
     }
 
   if (error_found)
@@ -4022,6 +3995,7 @@  cgraph_node::get_fun () const
 void
 cgraph_c_finalize (void)
 {
+  nested_function_info::release ();
   symtab = NULL;
 
   x_cgraph_nodes_queue = NULL;
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 96d6cf609fe..c953a1b6711 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -921,7 +921,7 @@  struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
   /* Constructor.  */
   explicit cgraph_node (int uid)
     : symtab_node (SYMTAB_FUNCTION), callees (NULL), callers (NULL),
-      indirect_calls (NULL), origin (NULL), nested (NULL), next_nested (NULL),
+      indirect_calls (NULL),
       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),
@@ -1161,9 +1161,6 @@  struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
   /* Return the DECL_STRUCT_FUNCTION of the function.  */
   struct function *get_fun () const;
 
-  /* cgraph_node is no longer nested function; update cgraph accordingly.  */
-  void unnest (void);
-
   /* Bring cgraph node local.  */
   void make_local (void);
 
@@ -1436,13 +1433,6 @@  struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
   /* List of edges representing indirect calls with a yet undetermined
      callee.  */
   cgraph_edge *indirect_calls;
-  /* For nested functions points to function the node is nested in.  */
-  cgraph_node *origin;
-  /* Points to first nested function, if any.  */
-  cgraph_node *nested;
-  /* Pointer to the next function with same origin, if any.  */
-  cgraph_node *next_nested;
-  /* Pointer to the next clone.  */
   cgraph_node *next_sibling_clone;
   cgraph_node *prev_sibling_clone;
   cgraph_node *clones;
diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
index db61c218297..f920dcb4c29 100644
--- a/gcc/cgraphclones.c
+++ b/gcc/cgraphclones.c
@@ -382,13 +382,7 @@  cgraph_node::create_clone (tree new_decl, profile_count prof_count,
     }
   new_node->decl = new_decl;
   new_node->register_symbol ();
-  new_node->origin = origin;
   new_node->lto_file_data = lto_file_data;
-  if (new_node->origin)
-    {
-      new_node->next_nested = new_node->origin->nested;
-      new_node->origin->nested = new_node;
-    }
   new_node->analyzed = analyzed;
   new_node->definition = definition;
   new_node->versionable = versionable;
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 19ae8763373..05713c28cf0 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -673,9 +673,8 @@  cgraph_node::analyze (void)
       /* Lower the function.  */
       if (!lowered)
 	{
-	  if (nested)
+	  if (first_nested_function (this))
 	    lower_nested_functions (decl);
-	  gcc_assert (!nested);
 
 	  gimple_register_cfg_hooks ();
 	  bitmap_obstack_initialize (NULL);
@@ -2343,14 +2342,11 @@  cgraph_node::expand (void)
     }
 
   gimple_set_body (decl, NULL);
-  if (DECL_STRUCT_FUNCTION (decl) == 0
-      && !cgraph_node::get (decl)->origin)
+  if (DECL_STRUCT_FUNCTION (decl) == 0)
     {
       /* Stop pointing to the local nodes about to be freed.
 	 But DECL_INITIAL must remain nonzero so we know this
-	 was an actual function definition.
-	 For a nested function, this is done in c_pop_function_context.
-	 If rest_of_compilation set this to 0, leave it 0.  */
+	 was an actual function definition.  */
       if (DECL_INITIAL (decl) != 0)
 	DECL_INITIAL (decl) = error_mark_node;
     }
@@ -3001,6 +2997,9 @@  symbol_table::finalize_compilation_unit (void)
   /* Gimplify and lower thunks.  */
   analyze_functions (/*first_time=*/false);
 
+  /* All nested functions should be lowered now.  */
+  nested_function_info::release ();
+
   /* Offloading requires LTO infrastructure.  */
   if (!in_lto_p && g->have_offload)
     flag_generate_offload = 1;
diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc
index 161a85a842b..1167462c305 100644
--- a/gcc/d/decl.cc
+++ b/gcc/d/decl.cc
@@ -53,6 +53,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "function.h"
 #include "debug.h"
 #include "tree-pretty-print.h"
+#include "tree-nested.h"
 
 #include "d-tree.h"
 
@@ -1280,8 +1281,8 @@  get_symbol_decl (Declaration *decl)
 	 all static chain passing is handled by the front-end.  Do this even
 	 if we are not emitting the body.  */
       struct cgraph_node *node = cgraph_node::get_create (decl->csym);
-      if (node->origin)
-	node->unnest ();
+      if (nested_function_origin (node))
+	unnest_function (node);
     }
 
   /* Mark compiler generated temporaries as artificial.  */
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index fa89e797940..29f385c9368 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -67,6 +67,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "dbgcnt.h"
 #include "omp-offload.h"
 #include "context.h"
+#include "tree-nested.h"
 
 /* Hash set of poisoned variables in a bind expr.  */
 static hash_set<tree> *asan_poisoned_variables = NULL;
@@ -959,7 +960,8 @@  unshare_body (tree fndecl)
   delete visited;
 
   if (cgn)
-    for (cgn = cgn->nested; cgn; cgn = cgn->next_nested)
+    for (cgn = first_nested_function (cgn); cgn;
+	 cgn = next_nested_function (cgn))
       unshare_body (cgn->decl);
 }
 
@@ -1002,7 +1004,8 @@  unvisit_body (tree fndecl)
   unmark_visited (&DECL_SIZE_UNIT (DECL_RESULT (fndecl)));
 
   if (cgn)
-    for (cgn = cgn->nested; cgn; cgn = cgn->next_nested)
+    for (cgn = first_nested_function (cgn);
+	 cgn; cgn = next_nested_function (cgn))
       unvisit_body (cgn->decl);
 }
 
diff --git a/gcc/ipa-pure-const.c b/gcc/ipa-pure-const.c
index 1af3206056e..70930a5eac0 100644
--- a/gcc/ipa-pure-const.c
+++ b/gcc/ipa-pure-const.c
@@ -1152,6 +1152,9 @@  funct_state_summary_t::insert (cgraph_node *node, funct_state_d *state)
       new (state) funct_state_d (*a);
       free (a);
     }
+  else
+    /* Do not keep stale summaries.  */
+    funct_state_summaries->remove (node);
 }
 
 /* Called when new clone is inserted to callgraph late.  */
diff --git a/gcc/omp-offload.c b/gcc/omp-offload.c
index 590007b943c..3e9c31d2cbe 100644
--- a/gcc/omp-offload.c
+++ b/gcc/omp-offload.c
@@ -334,7 +334,8 @@  omp_discover_implicit_declare_target (void)
 	else if (DECL_STRUCT_FUNCTION (node->decl)
 		 && DECL_STRUCT_FUNCTION (node->decl)->has_omp_target)
 	  worklist.safe_push (node->decl);
-	for (cgn = node->nested; cgn; cgn = cgn->next_nested)
+	for (cgn = first_nested_function (node);
+	     cgn; cgn = next_nested_function (cgn))
 	  if (omp_declare_target_fn_p (cgn->decl))
 	    worklist.safe_push (cgn->decl);
 	  else if (DECL_STRUCT_FUNCTION (cgn->decl)
diff --git a/gcc/tree-nested.c b/gcc/tree-nested.c
index f74a727dea1..433f37fffe3 100644
--- a/gcc/tree-nested.c
+++ b/gcc/tree-nested.c
@@ -42,7 +42,100 @@ 
 #include "gimple-low.h"
 #include "gomp-constants.h"
 #include "diagnostic.h"
+#include "alloc-pool.h"
+#include "tree-nested.h"
+#include "symbol-summary.h"
 
+/* Summary of nested functions.  */
+static function_summary <nested_function_info *>
+   *nested_function_sum = NULL;
+
+/* Return nested_function_info, if available.  */
+nested_function_info *
+nested_function_info::get (cgraph_node *node)
+{
+  if (!nested_function_sum)
+    return NULL;
+  return nested_function_sum->get (node);
+}
+
+/* Return nested_function_info possibly creating new one.  */
+nested_function_info *
+nested_function_info::get_create (cgraph_node *node)
+{
+  if (!nested_function_sum)
+    nested_function_sum = new function_summary <nested_function_info *>
+				 (symtab);
+  return nested_function_sum->get_create (node);
+}
+
+/* cgraph_node is no longer nested function; update cgraph accordingly.  */
+void
+unnest_function (cgraph_node *node)
+{
+  nested_function_info *info = nested_function_info::get (node);
+  cgraph_node **node2 = &nested_function_info::get
+		(nested_function_origin (node))->nested;
+
+  gcc_checking_assert (info->origin);
+  while (*node2 != node)
+    node2 = &nested_function_info::get (*node2)->next_nested;
+  *node2 = info->next_nested;
+  info->next_nested = NULL;
+  info->origin = NULL;
+  nested_function_sum->remove (node);
+}
+
+/* Destructor: unlink function from nested function lists.  */
+nested_function_info::~nested_function_info ()
+{
+  cgraph_node *next;
+  for (cgraph_node *n = nested; n; n = next)
+    {
+      nested_function_info *info = nested_function_info::get (n);
+      next = info->next_nested;
+      info->origin = NULL;
+      info->next_nested = NULL;
+    }
+  nested = NULL;
+  if (origin)
+    {
+      cgraph_node **node2
+	     = &nested_function_info::get (origin)->nested;
+
+      nested_function_info *info;
+      while ((info = nested_function_info::get (*node2)) != this && info)
+	node2 = &info->next_nested;
+      *node2 = next_nested;
+    }
+}
+
+/* Free nested function info summaries.  */
+void
+nested_function_info::release ()
+{
+  if (nested_function_sum)
+    delete (nested_function_sum);
+  nested_function_sum = NULL;
+}
+
+/* If NODE is nested function, record it.  */
+void
+maybe_record_nested_function (cgraph_node *node)
+{
+  if (DECL_CONTEXT (node->decl)
+      && TREE_CODE (DECL_CONTEXT (node->decl)) == FUNCTION_DECL)
+    {
+      cgraph_node *origin = cgraph_node::get_create (DECL_CONTEXT (node->decl));
+      nested_function_info *info = nested_function_info::get_create (node);
+      nested_function_info *origin_info
+		 = nested_function_info::get_create (origin);
+
+      info->origin = origin;
+      info->next_nested = origin_info->nested;
+      origin_info->nested = node;
+    }
+}
 
 /* The object of this pass is to lower the representation of a set of nested
    functions in order to expose all of the gory details of the various
@@ -586,7 +679,7 @@  lookup_element_for_decl (struct nesting_info *info, tree decl,
     *slot = build_tree_list (NULL_TREE, NULL_TREE);
 
   return (tree) *slot;
-} 
+}
 
 /* Given DECL, a nested function, create a field in the non-local
    frame structure for this function.  */
@@ -817,7 +910,8 @@  check_for_nested_with_variably_modified (tree fndecl, tree orig_fndecl)
   struct cgraph_node *cgn = cgraph_node::get (fndecl);
   tree arg;
 
-  for (cgn = cgn->nested; cgn ; cgn = cgn->next_nested)
+  for (cgn = first_nested_function (cgn); cgn;
+       cgn = next_nested_function (cgn))
     {
       for (arg = DECL_ARGUMENTS (cgn->decl); arg; arg = DECL_CHAIN (arg))
 	if (variably_modified_type_p (TREE_TYPE (arg), orig_fndecl))
@@ -845,7 +939,8 @@  create_nesting_tree (struct cgraph_node *cgn)
   info->context = cgn->decl;
   info->thunk_p = cgn->thunk.thunk_p;
 
-  for (cgn = cgn->nested; cgn ; cgn = cgn->next_nested)
+  for (cgn = first_nested_function (cgn); cgn;
+       cgn = next_nested_function (cgn))
     {
       struct nesting_info *sub = create_nesting_tree (cgn);
       sub->outer = info;
@@ -3498,9 +3593,9 @@  unnest_nesting_tree_1 (struct nesting_info *root)
 
   /* For nested functions update the cgraph to reflect unnesting.
      We also delay finalizing of these functions up to this point.  */
-  if (node->origin)
+  if (nested_function_info::get (node)->origin)
     {
-       node->unnest ();
+       unnest_function (node);
        if (!root->thunk_p)
 	 cgraph_node::finalize_function (root->context, true);
     }
@@ -3541,7 +3636,8 @@  gimplify_all_functions (struct cgraph_node *root)
   struct cgraph_node *iter;
   if (!gimple_body (root->decl))
     gimplify_function_tree (root->decl);
-  for (iter = root->nested; iter; iter = iter->next_nested)
+  for (iter = first_nested_function (root); iter;
+       iter = next_nested_function (iter))
     if (!iter->thunk.thunk_p)
       gimplify_all_functions (iter);
 }
@@ -3557,7 +3653,7 @@  lower_nested_functions (tree fndecl)
 
   /* If there are no nested functions, there's nothing to do.  */
   cgn = cgraph_node::get (fndecl);
-  if (!cgn->nested)
+  if (!first_nested_function (cgn))
     return;
 
   gimplify_all_functions (cgn);
diff --git a/gcc/tree-nested.h b/gcc/tree-nested.h
index 4ee56f49f9e..bdef4160ffb 100644
--- a/gcc/tree-nested.h
+++ b/gcc/tree-nested.h
@@ -24,4 +24,66 @@  extern tree build_addr (tree);
 extern void insert_field_into_struct (tree, tree);
 extern void lower_nested_functions (tree);
 
+class nested_function_info
+{
+public:
+  /* Constructor.  */
+  nested_function_info ()
+    : origin (NULL),
+      nested (NULL),
+      next_nested (NULL)
+  {
+  }
+  /* Copy constructor.  We can not simply copy the structure,
+     because the linked lists would go wrong.  However we should never
+     need that.  */
+  nested_function_info (const nested_function_info &)
+  {
+     gcc_unreachable ();
+  }
+  ~nested_function_info ();
+
+  /* Return nested_function_info, if available.  */
+  static nested_function_info *get (cgraph_node *node);
+
+  /* Return nested_function_info possibly creating new one.  */
+  static nested_function_info *get_create (cgraph_node *node);
+
+  /* Release all nested_function_infos.  */
+  static void release (void);
+
+  /* For nested functions points to function the node is nested in.  */
+  cgraph_node *origin;
+  /* Points to first nested function, if any.  */
+  cgraph_node *nested;
+  /* Pointer to the next function with same origin, if any.  */
+  cgraph_node *next_nested;
+};
+
+extern void maybe_record_nested_function (cgraph_node *node);
+extern void unnest_function (cgraph_node *node);
+
+/* If there are functions nested in NODE, return first one.  */
+inline cgraph_node *
+first_nested_function (cgraph_node *node)
+{
+  nested_function_info *info = nested_function_info::get (node);
+  return info ? info->nested : NULL;
+}
+
+/* Return next nested function (used to iterate from first_nested_function).  */
+inline cgraph_node *
+next_nested_function (cgraph_node *node)
+{
+  return nested_function_info::get (node)->next_nested;
+}
+
+/* Return origin of nested function (and NULL otherwise).  */
+inline cgraph_node *
+nested_function_origin (cgraph_node *node)
+{
+  nested_function_info *info = nested_function_info::get (node);
+  return info ? info->origin : NULL;
+}
+
 #endif /* GCC_TREE_NESTED_H */