X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;ds=sidebyside;f=gcc%2Fipa.c;h=89fb1da50fd42fd0e3e926c530ec699822efbaa1;hb=a6ef2ac9d92d85ff03ecc40c4aa80269289fbbd6;hp=6dc23ae9e624383ed9729250b69fd335cae7ec15;hpb=b2b293775a27c9f0099e9ccbf4ff965e79313c68;p=gcc.git diff --git a/gcc/ipa.c b/gcc/ipa.c index 6dc23ae9e62..89fb1da50fd 100644 --- a/gcc/ipa.c +++ b/gcc/ipa.c @@ -1,5 +1,5 @@ /* Basic IPA optimizations and utilities. - Copyright (C) 2003-2015 Free Software Foundation, Inc. + Copyright (C) 2003-2018 Free Software Foundation, Inc. This file is part of GCC. @@ -21,37 +21,30 @@ along with GCC; see the file COPYING3. If not see #include "system.h" #include "coretypes.h" #include "backend.h" +#include "target.h" #include "tree.h" #include "gimple.h" -#include "hard-reg-set.h" -#include "alias.h" -#include "options.h" -#include "fold-const.h" -#include "calls.h" +#include "alloc-pool.h" +#include "tree-pass.h" #include "stringpool.h" #include "cgraph.h" -#include "tree-pass.h" #include "gimplify.h" -#include "flags.h" -#include "target.h" #include "tree-iterator.h" #include "ipa-utils.h" -#include "alloc-pool.h" #include "symbol-summary.h" +#include "tree-vrp.h" #include "ipa-prop.h" -#include "ipa-inline.h" -#include "tree-inline.h" -#include "profile.h" -#include "params.h" -#include "internal-fn.h" +#include "ipa-fnsummary.h" #include "dbgcnt.h" - +#include "debug.h" +#include "stringpool.h" +#include "attribs.h" /* Return true when NODE has ADDR reference. */ static bool has_addr_references_p (struct cgraph_node *node, - void *data ATTRIBUTE_UNUSED) + void *) { int i; struct ipa_ref *ref = NULL; @@ -62,6 +55,14 @@ has_addr_references_p (struct cgraph_node *node, return false; } +/* Return true when NODE can be target of an indirect call. */ + +static bool +is_indirect_call_target_p (struct cgraph_node *node, void *) +{ + return node->indirect_call_target; +} + /* Look for all functions inlined to NODE and update their inlined_to pointers to INLINED_TO. */ @@ -100,12 +101,32 @@ enqueue_node (symtab_node *node, symtab_node **first, *first = node; } +/* Return true if NODE may get inlined later. + This is used to keep DECL_EXTERNAL function bodies around long enough + so inliner can proces them. */ + +static bool +possible_inline_candidate_p (symtab_node *node) +{ + if (symtab->state >= IPA_SSA_AFTER_INLINING) + return false; + cgraph_node *cnode = dyn_cast (node); + if (!cnode) + return false; + if (DECL_UNINLINABLE (cnode->decl)) + return false; + if (opt_for_fn (cnode->decl, optimize)) + return true; + if (symtab->state >= IPA_SSA) + return false; + return lookup_attribute ("always_inline", DECL_ATTRIBUTES (node->decl)); +} + /* Process references. */ static void process_references (symtab_node *snode, symtab_node **first, - bool before_inlining_p, hash_set *reachable) { int i; @@ -117,28 +138,22 @@ process_references (symtab_node *snode, if (node->definition && !node->in_other_partition && ((!DECL_EXTERNAL (node->decl) || node->alias) - || (((before_inlining_p - && ((TREE_CODE (node->decl) != FUNCTION_DECL - && optimize) - || (TREE_CODE (node->decl) == FUNCTION_DECL - && opt_for_fn (body->decl, optimize)) - || (symtab->state < IPA_SSA - && lookup_attribute - ("always_inline", - DECL_ATTRIBUTES (body->decl)))))) + || (possible_inline_candidate_p (node) /* We use variable constructors during late compilation for constant folding. Keep references alive so partitioning knows about potential references. */ - || (TREE_CODE (node->decl) == VAR_DECL - && flag_wpa - && ctor_for_folding (node->decl) - != error_mark_node)))) + || (VAR_P (node->decl) + && (flag_wpa + || flag_incremental_link + == INCREMENTAL_LINK_LTO) + && dyn_cast (node) + ->ctor_useable_for_folding_p ())))) { /* Be sure that we will not optimize out alias target body. */ if (DECL_EXTERNAL (node->decl) && node->alias - && before_inlining_p) + && symtab->state < IPA_SSA_AFTER_INLINING) reachable->add (body); reachable->add (node); } @@ -158,8 +173,7 @@ static void walk_polymorphic_call_targets (hash_set *reachable_call_targets, struct cgraph_edge *edge, symtab_node **first, - hash_set *reachable, - bool before_inlining_p) + hash_set *reachable) { unsigned int i; void *cache_token; @@ -182,23 +196,23 @@ walk_polymorphic_call_targets (hash_set *reachable_call_targets, (TYPE_METHOD_BASETYPE (TREE_TYPE (n->decl)))) continue; - symtab_node *body = n->function_symbol (); + n->indirect_call_target = true; + symtab_node *body = n->function_symbol (); /* Prior inlining, keep alive bodies of possible targets for devirtualization. */ - if (n->definition - && (before_inlining_p - && opt_for_fn (body->decl, optimize) - && opt_for_fn (body->decl, flag_devirtualize))) - { - /* Be sure that we will not optimize out alias target - body. */ - if (DECL_EXTERNAL (n->decl) - && n->alias - && before_inlining_p) - reachable->add (body); - reachable->add (n); - } + if (n->definition + && (possible_inline_candidate_p (body) + && opt_for_fn (body->decl, flag_devirtualize))) + { + /* Be sure that we will not optimize out alias target + body. */ + if (DECL_EXTERNAL (n->decl) + && n->alias + && symtab->state < IPA_SSA_AFTER_INLINING) + reachable->add (body); + reachable->add (n); + } /* Even after inlining we want to keep the possible targets in the boundary, so late passes can still produce direct call even if the chance for inlining is lost. */ @@ -222,29 +236,17 @@ walk_polymorphic_call_targets (hash_set *reachable_call_targets, (builtin_decl_implicit (BUILT_IN_UNREACHABLE)); if (dump_enabled_p ()) - { - location_t locus; - if (edge->call_stmt) - locus = gimple_location (edge->call_stmt); - else - locus = UNKNOWN_LOCATION; - dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus, - "devirtualizing call in %s/%i to %s/%i\n", - edge->caller->name (), edge->caller->order, - target->name (), - target->order); + { + dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, edge->call_stmt, + "devirtualizing call in %s to %s\n", + edge->caller->dump_name (), + target->dump_name ()); } edge = edge->make_direct (target); - if (inline_summaries) - inline_update_overall_summary (node); + if (ipa_fn_summaries) + ipa_update_overall_fn_summary (node); else if (edge->call_stmt) - { - edge->redirect_call_stmt_to_callee (); - - /* Call to __builtin_unreachable shouldn't be instrumented. */ - if (!targets.length ()) - gimple_call_set_with_bounds (edge->call_stmt, false); - } + edge->redirect_call_stmt_to_callee (); } } } @@ -312,8 +314,6 @@ symbol_table::remove_unreachable_nodes (FILE *file) hash_set reachable; hash_set body_needed_for_clonning; hash_set reachable_call_targets; - bool before_inlining_p = symtab->state < (!optimize ? IPA_SSA - : IPA_SSA_AFTER_INLINING); timevar_push (TV_IPA_UNREACHABLE); build_type_inheritance_graph (); @@ -333,6 +333,7 @@ symbol_table::remove_unreachable_nodes (FILE *file) FOR_EACH_FUNCTION (node) { node->used_as_abstract_origin = false; + node->indirect_call_target = false; if (node->definition && !node->global.inlined_to && !node->in_other_partition @@ -404,7 +405,7 @@ symbol_table::remove_unreachable_nodes (FILE *file) enqueue_node (next, &first, &reachable); } /* Mark references as reachable. */ - process_references (node, &first, before_inlining_p, &reachable); + process_references (node, &first, &reachable); } if (cgraph_node *cnode = dyn_cast (node)) @@ -424,8 +425,7 @@ symbol_table::remove_unreachable_nodes (FILE *file) next = e->next_callee; if (e->indirect_info->polymorphic) walk_polymorphic_call_targets (&reachable_call_targets, - e, &first, &reachable, - before_inlining_p); + e, &first, &reachable); } } for (e = cnode->callees; e; e = e->next_callee) @@ -436,18 +436,13 @@ symbol_table::remove_unreachable_nodes (FILE *file) && (!e->inline_failed || !DECL_EXTERNAL (e->callee->decl) || e->callee->alias - || (before_inlining_p - && (opt_for_fn (body->decl, optimize) - || (symtab->state < IPA_SSA - && lookup_attribute - ("always_inline", - DECL_ATTRIBUTES (body->decl))))))) + || possible_inline_candidate_p (e->callee))) { /* Be sure that we will not optimize out alias target body. */ if (DECL_EXTERNAL (e->callee->decl) && e->callee->alias - && before_inlining_p) + && symtab->state < IPA_SSA_AFTER_INLINING) reachable.add (body); reachable.add (e->callee); } @@ -459,20 +454,6 @@ symbol_table::remove_unreachable_nodes (FILE *file) if (cnode->global.inlined_to) body_needed_for_clonning.add (cnode->decl); - /* For instrumentation clones we always need original - function node for proper LTO privatization. */ - if (cnode->instrumentation_clone - && cnode->definition) - { - gcc_assert (cnode->instrumented_version || in_lto_p); - if (cnode->instrumented_version) - { - enqueue_node (cnode->instrumented_version, &first, - &reachable); - reachable.add (cnode->instrumented_version); - } - } - /* For non-inline clones, force their origins to the boundary and ensure that body is not removed. */ while (cnode->clone_of) @@ -527,7 +508,7 @@ symbol_table::remove_unreachable_nodes (FILE *file) if (!node->aux) { if (file) - fprintf (file, " %s/%i", node->name (), node->order); + fprintf (file, " %s", node->dump_name ()); node->remove (); changed = true; } @@ -547,12 +528,13 @@ symbol_table::remove_unreachable_nodes (FILE *file) if (node->definition && !node->alias && !node->thunk.thunk_p) { if (file) - fprintf (file, " %s/%i", node->name (), node->order); + fprintf (file, " %s", node->dump_name ()); node->body_removed = true; node->analyzed = false; node->definition = false; node->cpp_implicit_alias = false; node->alias = false; + node->transparent_alias = false; node->thunk.thunk_p = false; node->weakref = false; /* After early inlining we drop always_inline attributes on @@ -613,13 +595,18 @@ symbol_table::remove_unreachable_nodes (FILE *file) while (vnode->iterate_direct_aliases (0, ref)) { if (file) - fprintf (file, " %s/%i", ref->referred->name (), - ref->referred->order); + fprintf (file, " %s", ref->referred->dump_name ()); ref->referring->remove (); } if (file) - fprintf (file, " %s/%i", vnode->name (), vnode->order); + fprintf (file, " %s", vnode->dump_name ()); vnext = next_variable (vnode); + /* Signal removal to the debug machinery. */ + if (! flag_wpa || flag_incremental_link == INCREMENTAL_LINK_LTO) + { + vnode->definition = false; + (*debug_hooks->late_global_decl) (vnode->decl); + } vnode->remove (); changed = true; } @@ -633,8 +620,8 @@ symbol_table::remove_unreachable_nodes (FILE *file) changed = true; } /* Keep body if it may be useful for constant folding. */ - if ((init = ctor_for_folding (vnode->decl)) == error_mark_node - && !POINTER_BOUNDS_P (vnode->decl)) + if ((flag_wpa || flag_incremental_link == INCREMENTAL_LINK_LTO) + || ((init = ctor_for_folding (vnode->decl)) == error_mark_node)) vnode->remove_initializer (); else DECL_INITIAL (vnode->decl) = init; @@ -659,16 +646,20 @@ symbol_table::remove_unreachable_nodes (FILE *file) && !node->used_from_other_partition) { if (!node->call_for_symbol_and_aliases - (has_addr_references_p, NULL, true) - && (!node->instrumentation_clone - || !node->instrumented_version - || !node->instrumented_version->address_taken)) + (has_addr_references_p, NULL, true)) { if (file) fprintf (file, " %s", node->name ()); node->address_taken = false; changed = true; - if (node->local_p ()) + if (node->local_p () + /* Virtual functions may be kept in cgraph just because + of possible later devirtualization. Do not mark them as + local too early so we won't optimize them out before + we are done with polymorphic call analysis. */ + && (symtab->state >= IPA_SSA_AFTER_INLINING + || !node->call_for_symbol_and_aliases + (is_indirect_call_target_p, NULL, true))) { node->local.local = true; if (file) @@ -682,7 +673,7 @@ symbol_table::remove_unreachable_nodes (FILE *file) symtab_node::checking_verify_symtab_nodes (); /* If we removed something, perhaps profile could be improved. */ - if (changed && optimize && inline_edge_summary_vec.exists ()) + if (changed && (optimize || in_lto_p) && ipa_call_summaries) FOR_EACH_DEFINED_FUNCTION (node) ipa_propagate_frequency (node); @@ -723,8 +714,6 @@ process_references (varpool_node *vnode, process_references (dyn_cast (ref->referring), written, address_taken, read, explicit_refs); break; - case IPA_REF_CHKP: - gcc_unreachable (); } } @@ -743,7 +732,7 @@ bool set_writeonly_bit (varpool_node *vnode, void *data) { vnode->writeonly = true; - if (optimize) + if (optimize || in_lto_p) { DECL_INITIAL (vnode->decl) = NULL; if (!vnode->alias) @@ -766,10 +755,10 @@ clear_addressable_bit (varpool_node *vnode, void *data ATTRIBUTE_UNUSED) return false; } -/* Discover variables that have no longer address taken or that are read only - and update their flags. +/* Discover variables that have no longer address taken, are read-only or + write-only and update their flags. - Return true when unreachable symbol removan should be done. + Return true when unreachable symbol removal should be done. FIXME: This can not be done in between gimplify and omp_expand since readonly flag plays role on what is shared and what is not. Currently we do @@ -778,8 +767,11 @@ clear_addressable_bit (varpool_node *vnode, void *data ATTRIBUTE_UNUSED) make sense to do it before early optimizations. */ bool -ipa_discover_readonly_nonaddressable_vars (void) +ipa_discover_variable_flags (void) { + if (!flag_ipa_reference_addressable) + return false; + bool remove_p = false; varpool_node *vnode; if (dump_file) @@ -829,54 +821,9 @@ ipa_discover_readonly_nonaddressable_vars (void) return remove_p; } -/* Free inline summary. */ - -namespace { - -const pass_data pass_data_ipa_free_inline_summary = -{ - SIMPLE_IPA_PASS, /* type */ - "free-inline-summary", /* name */ - OPTGROUP_NONE, /* optinfo_flags */ - TV_IPA_FREE_INLINE_SUMMARY, /* tv_id */ - 0, /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - 0, /* todo_flags_start */ - /* Early optimizations may make function unreachable. We can not - remove unreachable functions as part of the ealry opts pass because - TODOs are run before subpasses. Do it here. */ - ( TODO_remove_functions | TODO_dump_symtab ), /* todo_flags_finish */ -}; - -class pass_ipa_free_inline_summary : public simple_ipa_opt_pass -{ -public: - pass_ipa_free_inline_summary (gcc::context *ctxt) - : simple_ipa_opt_pass (pass_data_ipa_free_inline_summary, ctxt) - {} - - /* opt_pass methods: */ - virtual unsigned int execute (function *) - { - inline_free_summary (); - return 0; - } - -}; // class pass_ipa_free_inline_summary - -} // anon namespace - -simple_ipa_opt_pass * -make_pass_ipa_free_inline_summary (gcc::context *ctxt) -{ - return new pass_ipa_free_inline_summary (ctxt); -} - /* Generate and emit a static constructor or destructor. WHICH must - be one of 'I' (for a constructor), 'D' (for a destructor), 'P' - (for chp static vars constructor) or 'B' (for chkp static bounds - constructor). BODY is a STATEMENT_LIST containing GENERIC + be one of 'I' (for a constructor), 'D' (for a destructor). + BODY is a STATEMENT_LIST containing GENERIC statements. PRIORITY is the initialization priority for this constructor or destructor. @@ -927,6 +874,7 @@ cgraph_build_static_cdtor_1 (char which, tree body, int priority, bool final) DECL_UNINLINABLE (decl) = 1; DECL_INITIAL (decl) = make_node (BLOCK); + BLOCK_SUPERCONTEXT (DECL_INITIAL (decl)) = decl; TREE_USED (DECL_INITIAL (decl)) = 1; DECL_SOURCE_LOCATION (decl) = input_location; @@ -938,20 +886,6 @@ cgraph_build_static_cdtor_1 (char which, tree body, int priority, bool final) DECL_STATIC_CONSTRUCTOR (decl) = 1; decl_init_priority_insert (decl, priority); break; - case 'P': - DECL_STATIC_CONSTRUCTOR (decl) = 1; - DECL_ATTRIBUTES (decl) = tree_cons (get_identifier ("chkp ctor"), - NULL, - NULL_TREE); - decl_init_priority_insert (decl, priority); - break; - case 'B': - DECL_STATIC_CONSTRUCTOR (decl) = 1; - DECL_ATTRIBUTES (decl) = tree_cons (get_identifier ("bnd_legacy"), - NULL, - NULL_TREE); - decl_init_priority_insert (decl, priority); - break; case 'D': DECL_STATIC_DESTRUCTOR (decl) = 1; decl_fini_priority_insert (decl, priority); @@ -969,9 +903,8 @@ cgraph_build_static_cdtor_1 (char which, tree body, int priority, bool final) } /* Generate and emit a static constructor or destructor. WHICH must - be one of 'I' (for a constructor), 'D' (for a destructor), 'P' - (for chkp static vars constructor) or 'B' (for chkp static bounds - constructor). BODY is a STATEMENT_LIST containing GENERIC + be one of 'I' (for a constructor) or 'D' (for a destructor). + BODY is a STATEMENT_LIST containing GENERIC statements. PRIORITY is the initialization priority for this constructor or destructor. */ @@ -981,11 +914,6 @@ cgraph_build_static_cdtor (char which, tree body, int priority) cgraph_build_static_cdtor_1 (which, body, priority, false); } -/* A vector of FUNCTION_DECLs declared as static constructors. */ -static vec static_ctors; -/* A vector of FUNCTION_DECLs declared as static destructors. */ -static vec static_dtors; - /* When target does not have ctors and dtors, we call all constructor and destructor by special initialization/destruction function recognized by collect2. @@ -994,12 +922,12 @@ static vec static_dtors; destructors and turn them into normal functions. */ static void -record_cdtor_fn (struct cgraph_node *node) +record_cdtor_fn (struct cgraph_node *node, vec *ctors, vec *dtors) { if (DECL_STATIC_CONSTRUCTOR (node->decl)) - static_ctors.safe_push (node->decl); + ctors->safe_push (node->decl); if (DECL_STATIC_DESTRUCTOR (node->decl)) - static_dtors.safe_push (node->decl); + dtors->safe_push (node->decl); node = cgraph_node::get (node->decl); DECL_DISREGARD_INLINE_LIMITS (node->decl) = 1; } @@ -1010,7 +938,7 @@ record_cdtor_fn (struct cgraph_node *node) they are destructors. */ static void -build_cdtor (bool ctor_p, vec cdtors) +build_cdtor (bool ctor_p, const vec &cdtors) { size_t i,j; size_t len = cdtors.length (); @@ -1127,20 +1055,20 @@ compare_dtor (const void *p1, const void *p2) functions have magic names which are detected by collect2. */ static void -build_cdtor_fns (void) +build_cdtor_fns (vec *ctors, vec *dtors) { - if (!static_ctors.is_empty ()) + if (!ctors->is_empty ()) { gcc_assert (!targetm.have_ctors_dtors || in_lto_p); - static_ctors.qsort (compare_ctor); - build_cdtor (/*ctor_p=*/true, static_ctors); + ctors->qsort (compare_ctor); + build_cdtor (/*ctor_p=*/true, *ctors); } - if (!static_dtors.is_empty ()) + if (!dtors->is_empty ()) { gcc_assert (!targetm.have_ctors_dtors || in_lto_p); - static_dtors.qsort (compare_dtor); - build_cdtor (/*ctor_p=*/false, static_dtors); + dtors->qsort (compare_dtor); + build_cdtor (/*ctor_p=*/false, *dtors); } } @@ -1153,14 +1081,16 @@ build_cdtor_fns (void) static unsigned int ipa_cdtor_merge (void) { + /* A vector of FUNCTION_DECLs declared as static constructors. */ + auto_vec ctors; + /* A vector of FUNCTION_DECLs declared as static destructors. */ + auto_vec dtors; struct cgraph_node *node; FOR_EACH_DEFINED_FUNCTION (node) if (DECL_STATIC_CONSTRUCTOR (node->decl) || DECL_STATIC_DESTRUCTOR (node->decl)) - record_cdtor_fn (node); - build_cdtor_fns (); - static_ctors.release (); - static_dtors.release (); + record_cdtor_fn (node, &ctors, &dtors); + build_cdtor_fns (&ctors, &dtors); return 0; } @@ -1207,7 +1137,7 @@ pass_ipa_cdtor_merge::gate (function *) /* Perform the pass when we have no ctors/dtors support or at LTO time to merge multiple constructors into single function. */ - return !targetm.have_ctors_dtors || (optimize && in_lto_p); + return !targetm.have_ctors_dtors || in_lto_p; } } // anon namespace @@ -1377,8 +1307,8 @@ ipa_single_use (void) if (dump_file) { - fprintf (dump_file, "Variable %s/%i is used by single function\n", - var->name (), var->order); + fprintf (dump_file, "Variable %s is used by single function\n", + var->dump_name ()); } var->used_by_single_function = true; } @@ -1419,17 +1349,10 @@ public: {} /* opt_pass methods: */ - virtual bool gate (function *); virtual unsigned int execute (function *) { return ipa_single_use (); } }; // class pass_ipa_single_use -bool -pass_ipa_single_use::gate (function *) -{ - return optimize; -} - } // anon namespace ipa_opt_pass_d * @@ -1437,3 +1360,44 @@ make_pass_ipa_single_use (gcc::context *ctxt) { return new pass_ipa_single_use (ctxt); } + +/* Materialize all clones. */ + +namespace { + +const pass_data pass_data_materialize_all_clones = +{ + SIMPLE_IPA_PASS, /* type */ + "materialize-all-clones", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_IPA_OPT, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_materialize_all_clones : public simple_ipa_opt_pass +{ +public: + pass_materialize_all_clones (gcc::context *ctxt) + : simple_ipa_opt_pass (pass_data_materialize_all_clones, ctxt) + {} + + /* opt_pass methods: */ + virtual unsigned int execute (function *) + { + symtab->materialize_all_clones (); + return 0; + } + +}; // class pass_materialize_all_clones + +} // anon namespace + +simple_ipa_opt_pass * +make_pass_materialize_all_clones (gcc::context *ctxt) +{ + return new pass_materialize_all_clones (ctxt); +}