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 <hubi...@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 <hubi...@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 <hubi...@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 <hubi...@ucw.cz> * decl.cc: Include tree-nested.h (get_symbol_decl): Update for new nested function info. 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 */