diff options
author | Jan Hubicka <jh@suse.cz> | 2016-04-16 18:30:48 +0200 |
---|---|---|
committer | Jan Hubicka <hubicka@gcc.gnu.org> | 2016-04-16 16:30:48 +0000 |
commit | f13fe18b5b4881a457c2176214349d322b61e187 (patch) | |
tree | 4cef7161787b418b3d579ca1e2fc6cd70a647584 /gcc | |
parent | 06c3ddc04956997f9a45769cf44cc85f013e0245 (diff) | |
download | gcc-f13fe18b5b4881a457c2176214349d322b61e187.zip gcc-f13fe18b5b4881a457c2176214349d322b61e187.tar.gz gcc-f13fe18b5b4881a457c2176214349d322b61e187.tar.bz2 |
re PR c++/70018 (Possible issue around IPO and C++ comdats discovered as pure/const)
PR ipa/70018
* cgraph.c (cgraph_node::get_availability): Add REF parameter.
(cgraph_node::function_symbol): Likewise.
(cgraph_node::function_or_virtual_thunk_symbol): Likewise.
* cgraph.h (symtab_node::get_availabbility): Add REF parameter.
(symtab_node::ultimate_alias_target): Add REF parameter.
(symtab_node::binds_to_current_def_p): Declare.
(symtab_node;:ultimate_alias_target_1): Add REF parameter.
(cgraph_node::function_symbol): Likewise.
(cgraph_node::function_or_virtual_thunk_symbol): Likewise.
(cgraph_node::get_availability): Likewise.
(cgraph_edge::binds_to_current_def_p): New inline function.
(varpool_node::get_availability): Add REF parameter.
(varpool_node::ultimate_alias_target): Likewise.
* symtab.c (symtab_node::ultimate_alias_target_1): Likewise.
(symtab_node::binds_to_current_def_p): Likewise.
* varpool.c (varpool_node::get_availability): Likewise.
From-SVN: r235063
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 20 | ||||
-rw-r--r-- | gcc/cgraph.c | 55 | ||||
-rw-r--r-- | gcc/cgraph.h | 101 | ||||
-rw-r--r-- | gcc/symtab.c | 64 | ||||
-rw-r--r-- | gcc/varpool.c | 11 |
5 files changed, 199 insertions, 52 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index e64cd0f..98ced28 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,23 @@ +2016-04-15 Jan Hubicka <jh@suse.cz> + + PR ipa/70018 + * cgraph.c (cgraph_node::get_availability): Add REF parameter. + (cgraph_node::function_symbol): Likewise. + (cgraph_node::function_or_virtual_thunk_symbol): Likewise. + * cgraph.h (symtab_node::get_availabbility): Add REF parameter. + (symtab_node::ultimate_alias_target): Add REF parameter. + (symtab_node::binds_to_current_def_p): Declare. + (symtab_node;:ultimate_alias_target_1): Add REF parameter. + (cgraph_node::function_symbol): Likewise. + (cgraph_node::function_or_virtual_thunk_symbol): Likewise. + (cgraph_node::get_availability): Likewise. + (cgraph_edge::binds_to_current_def_p): New inline function. + (varpool_node::get_availability): Add REF parameter. + (varpool_node::ultimate_alias_target): Likewise. + * symtab.c (symtab_node::ultimate_alias_target_1): Likewise. + (symtab_node::binds_to_current_def_p): Likewise. + * varpool.c (varpool_node::get_availability): Likewise. + 2016-04-15 Kirill Yukhin <kirill.yukhin@intel.com> PR target/70662 diff --git a/gcc/cgraph.c b/gcc/cgraph.c index 4804081..0ced2db 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -2209,19 +2209,36 @@ cgraph_node::unnest (void) /* Return function availability. See cgraph.h for description of individual return values. */ enum availability -cgraph_node::get_availability (void) +cgraph_node::get_availability (symtab_node *ref) { + if (ref) + { + cgraph_node *cref = dyn_cast <cgraph_node *> (ref); + if (cref) + ref = cref->global.inlined_to; + } enum availability avail; if (!analyzed) avail = AVAIL_NOT_AVAILABLE; else if (local.local) avail = AVAIL_LOCAL; + else if (global.inlined_to) + avail = AVAIL_AVAILABLE; else if (transparent_alias) - ultimate_alias_target (&avail); + ultimate_alias_target (&avail, ref); else if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (decl))) avail = AVAIL_INTERPOSABLE; else if (!externally_visible) avail = AVAIL_AVAILABLE; + /* If this is a reference from symbol itself and there are no aliases, we + may be sure that the symbol was not interposed by soemthing else because + the symbol itself would be unreachable otherwise. + + Also comdat groups are always resolved in groups. */ + else if ((this == ref && !has_aliases_p ()) + || (ref && get_comdat_group () + && get_comdat_group () == ref->get_comdat_group ())) + avail = AVAIL_AVAILABLE; /* Inline functions are safe to be analyzed even if their symbol can be overwritten at runtime. It is not meaningful to enforce any sane behavior on replacing inline function by different body. */ @@ -2232,11 +2249,7 @@ cgraph_node::get_availability (void) care at least of two notable extensions - the COMDAT functions used to share template instantiations in C++ (this is symmetric to code cp_cannot_inline_tree_fn and probably shall be shared and - the inlinability hooks completely eliminated). - - ??? Does the C++ one definition rule allow us to always return - AVAIL_AVAILABLE here? That would be good reason to preserve this - bit. */ + the inlinability hooks completely eliminated). */ else if (decl_replaceable_p (decl) && !DECL_EXTERNAL (decl)) avail = AVAIL_INTERPOSABLE; @@ -3250,24 +3263,28 @@ cgraph_node::verify_cgraph_nodes (void) /* Walk the alias chain to return the function cgraph_node is alias of. Walk through thunks, too. - When AVAILABILITY is non-NULL, get minimal availability in the chain. */ + When AVAILABILITY is non-NULL, get minimal availability in the chain. + When REF is non-NULL, assume that reference happens in symbol REF + when determining the availability. */ cgraph_node * -cgraph_node::function_symbol (enum availability *availability) +cgraph_node::function_symbol (enum availability *availability, + struct symtab_node *ref) { - cgraph_node *node = ultimate_alias_target (availability); + cgraph_node *node = ultimate_alias_target (availability, ref); while (node->thunk.thunk_p) { + ref = node; node = node->callees->callee; if (availability) { enum availability a; - a = node->get_availability (); + a = node->get_availability (ref); if (a < *availability) *availability = a; } - node = node->ultimate_alias_target (availability); + node = node->ultimate_alias_target (availability, ref); } return node; } @@ -3275,25 +3292,29 @@ cgraph_node::function_symbol (enum availability *availability) /* Walk the alias chain to return the function cgraph_node is alias of. Walk through non virtual thunks, too. Thus we return either a function or a virtual thunk node. - When AVAILABILITY is non-NULL, get minimal availability in the chain. */ + When AVAILABILITY is non-NULL, get minimal availability in the chain. + When REF is non-NULL, assume that reference happens in symbol REF + when determining the availability. */ cgraph_node * cgraph_node::function_or_virtual_thunk_symbol - (enum availability *availability) + (enum availability *availability, + struct symtab_node *ref) { - cgraph_node *node = ultimate_alias_target (availability); + cgraph_node *node = ultimate_alias_target (availability, ref); while (node->thunk.thunk_p && !node->thunk.virtual_offset_p) { + ref = node; node = node->callees->callee; if (availability) { enum availability a; - a = node->get_availability (); + a = node->get_availability (ref); if (a < *availability) *availability = a; } - node = node->ultimate_alias_target (availability); + node = node->ultimate_alias_target (availability, ref); } return node; } diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 0d5806b..5b2b4bc 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -242,8 +242,11 @@ public: /* Walk the alias chain to return the symbol NODE is alias of. If NODE is not an alias, return NODE. - When AVAILABILITY is non-NULL, get minimal availability in the chain. */ - symtab_node *ultimate_alias_target (enum availability *avail = NULL); + When AVAILABILITY is non-NULL, get minimal availability in the chain. + When REF is non-NULL, assume that reference happens in symbol REF + when determining the availability. */ + symtab_node *ultimate_alias_target (enum availability *avail = NULL, + struct symtab_node *ref = NULL); /* Return next reachable static symbol with initializer after NODE. */ inline symtab_node *next_defined_symbol (void); @@ -287,8 +290,13 @@ public: /* Return the initialization priority. */ priority_type get_init_priority (); - /* Return availability of NODE. */ - enum availability get_availability (void); + /* Return availability of NODE when referenced from REF. */ + enum availability get_availability (symtab_node *ref = NULL); + + /* Return true if NODE binds to current definition in final executable + when referenced from REF. If REF is NULL return conservative value + for any reference. */ + bool binds_to_current_def_p (symtab_node *ref = NULL); /* Make DECL local. */ void make_decl_local (void); @@ -595,7 +603,8 @@ private: static bool noninterposable_alias (symtab_node *node, void *data); /* Worker for ultimate_alias_target. */ - symtab_node *ultimate_alias_target_1 (enum availability *avail = NULL); + symtab_node *ultimate_alias_target_1 (enum availability *avail = NULL, + symtab_node *ref = NULL); }; inline void @@ -867,15 +876,21 @@ public: /* Walk the alias chain to return the function cgraph_node is alias of. Walk through thunk, too. - When AVAILABILITY is non-NULL, get minimal availability in the chain. */ - cgraph_node *function_symbol (enum availability *avail = NULL); + When AVAILABILITY is non-NULL, get minimal availability in the chain. + When REF is non-NULL, assume that reference happens in symbol REF + when determining the availability. */ + cgraph_node *function_symbol (enum availability *avail = NULL, + struct symtab_node *ref = NULL); /* Walk the alias chain to return the function cgraph_node is alias of. Walk through non virtual thunks, too. Thus we return either a function or a virtual thunk node. - When AVAILABILITY is non-NULL, get minimal availability in the chain. */ + When AVAILABILITY is non-NULL, get minimal availability in the chain. + When REF is non-NULL, assume that reference happens in symbol REF + when determining the availability. */ cgraph_node *function_or_virtual_thunk_symbol - (enum availability *avail = NULL); + (enum availability *avail = NULL, + struct symtab_node *ref = NULL); /* Create node representing clone of N executed COUNT times. Decrease the execution counts from original node too. @@ -974,9 +989,12 @@ public: /* Given function symbol, walk the alias chain to return the function node is alias of. Do not walk through thunks. - When AVAILABILITY is non-NULL, get minimal availability in the chain. */ + When AVAILABILITY is non-NULL, get minimal availability in the chain. + When REF is non-NULL, assume that reference happens in symbol REF + when determining the availability. */ - cgraph_node *ultimate_alias_target (availability *availability = NULL); + 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 @@ -1089,7 +1107,7 @@ public: /* Return function availability. See cgraph.h for description of individual return values. */ - enum availability get_availability (void); + enum availability get_availability (symtab_node *ref = NULL); /* Set TREE_NOTHROW on cgraph_node's decl and on aliases of the node if any to NOTHROW. */ @@ -1689,6 +1707,9 @@ struct GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"), type. */ unsigned in_polymorphic_cdtor : 1; + /* Return true if call must bind to current definition. */ + bool binds_to_current_def_p (); + private: /* Remove the edge from the list of the callers of the callee. */ void remove_caller (void); @@ -1731,7 +1752,7 @@ public: void analyze (void); /* Return variable availability. */ - availability get_availability (void); + availability get_availability (symtab_node *ref = NULL); /* When doing LTO, read variable's constructor from disk if it is not already present. */ @@ -1742,9 +1763,11 @@ public: /* For given variable pool node, walk the alias chain to return the function the variable is alias of. Do not walk through thunks. - When AVAILABILITY is non-NULL, get minimal availability in the chain. */ + When AVAILABILITY is non-NULL, get minimal availability in the chain. + When REF is non-NULL, assume that reference happens in symbol REF + when determining the availability. */ inline varpool_node *ultimate_alias_target - (availability *availability = NULL); + (availability *availability = NULL, symtab_node *ref = NULL); /* Return node that alias is aliasing. */ inline varpool_node *get_alias_target (void); @@ -2888,30 +2911,36 @@ varpool_node::get_alias_target (void) /* Walk the alias chain to return the symbol NODE is alias of. If NODE is not an alias, return NODE. - When AVAILABILITY is non-NULL, get minimal availability in the chain. */ + When AVAILABILITY is non-NULL, get minimal availability in the chain. + When REF is non-NULL, assume that reference happens in symbol REF + when determining the availability. */ inline symtab_node * -symtab_node::ultimate_alias_target (enum availability *availability) +symtab_node::ultimate_alias_target (enum availability *availability, + symtab_node *ref) { if (!alias) { if (availability) - *availability = get_availability (); + *availability = get_availability (ref); return this; } - return ultimate_alias_target_1 (availability); + return ultimate_alias_target_1 (availability, ref); } /* Given function symbol, walk the alias chain to return the function node is alias of. Do not walk through thunks. - When AVAILABILITY is non-NULL, get minimal availability in the chain. */ + When AVAILABILITY is non-NULL, get minimal availability in the chain. + When REF is non-NULL, assume that reference happens in symbol REF + when determining the availability. */ inline cgraph_node * -cgraph_node::ultimate_alias_target (enum availability *availability) +cgraph_node::ultimate_alias_target (enum availability *availability, + symtab_node *ref) { cgraph_node *n = dyn_cast <cgraph_node *> - (symtab_node::ultimate_alias_target (availability)); + (symtab_node::ultimate_alias_target (availability, ref)); if (!n && availability) *availability = AVAIL_NOT_AVAILABLE; return n; @@ -2919,13 +2948,16 @@ cgraph_node::ultimate_alias_target (enum availability *availability) /* For given variable pool node, walk the alias chain to return the function the variable is alias of. Do not walk through thunks. - When AVAILABILITY is non-NULL, get minimal availability in the chain. */ + When AVAILABILITY is non-NULL, get minimal availability in the chain. + When REF is non-NULL, assume that reference happens in symbol REF + when determining the availability. */ inline varpool_node * -varpool_node::ultimate_alias_target (availability *availability) +varpool_node::ultimate_alias_target (availability *availability, + symtab_node *ref) { varpool_node *n = dyn_cast <varpool_node *> - (symtab_node::ultimate_alias_target (availability)); + (symtab_node::ultimate_alias_target (availability, ref)); if (!n && availability) *availability = AVAIL_NOT_AVAILABLE; @@ -2985,6 +3017,17 @@ cgraph_edge::remove_callee (void) callee->callers = next_caller; } +/* Return true if call must bind to current definition. */ + +inline bool +cgraph_edge::binds_to_current_def_p () +{ + if (callee) + return callee->binds_to_current_def_p (caller); + else + return NULL; +} + /* Return true if the TM_CLONE bit is set for a given FNDECL. */ static inline bool decl_is_tm_clone (const_tree fndecl) @@ -3030,15 +3073,15 @@ symtab_node::get_create (tree node) return cgraph_node::get_create (node); } -/* Return availability of NODE. */ +/* Return availability of NODE when referenced from REF. */ inline enum availability -symtab_node::get_availability (void) +symtab_node::get_availability (symtab_node *ref) { if (is_a <cgraph_node *> (this)) - return dyn_cast <cgraph_node *> (this)->get_availability (); + return dyn_cast <cgraph_node *> (this)->get_availability (ref); else - return dyn_cast <varpool_node *> (this)->get_availability (); + return dyn_cast <varpool_node *> (this)->get_availability (ref); } /* Call calback on symtab node and aliases associated to this node. diff --git a/gcc/symtab.c b/gcc/symtab.c index 3d3cc4f..e69dc17 100644 --- a/gcc/symtab.c +++ b/gcc/symtab.c @@ -1347,7 +1347,8 @@ symtab_node::copy_visibility_from (symtab_node *n) Assumes NODE is known to be alias. */ symtab_node * -symtab_node::ultimate_alias_target_1 (enum availability *availability) +symtab_node::ultimate_alias_target_1 (enum availability *availability, + symtab_node *ref) { bool transparent_p = false; @@ -1368,7 +1369,7 @@ symtab_node::ultimate_alias_target_1 (enum availability *availability) { transparent_p = transparent_alias; if (!transparent_p) - *availability = get_availability (); + *availability = get_availability (ref); else *availability = AVAIL_NOT_AVAILABLE; } @@ -1383,7 +1384,7 @@ symtab_node::ultimate_alias_target_1 (enum availability *availability) if (!availability || (!transparent_p && node->analyzed)) ; else if (node->analyzed && !node->transparent_alias) - *availability = node->get_availability (); + *availability = node->get_availability (ref); else *availability = AVAIL_NOT_AVAILABLE; return node; @@ -1391,7 +1392,7 @@ symtab_node::ultimate_alias_target_1 (enum availability *availability) if (node && availability && transparent_p && node->transparent_alias) { - *availability = node->get_availability (); + *availability = node->get_availability (ref); transparent_p = false; } } @@ -2206,3 +2207,58 @@ symbol_table::symbol_suffix_separator () return '_'; #endif } + +/* Return true when references to this symbol from REF must bind to current + definition in final executable. */ + +bool +symtab_node::binds_to_current_def_p (symtab_node *ref) +{ + if (!definition) + return false; + if (decl_binds_to_current_def_p (decl)) + return true; + + /* Inline clones always binds locally. */ + cgraph_node *cnode = dyn_cast <cgraph_node *> (this); + if (cnode && cnode->global.inlined_to) + return true; + + if (DECL_EXTERNAL (decl)) + return false; + + if (!externally_visible) + debug (); + gcc_assert (externally_visible); + + if (ref) + { + cgraph_node *cref = dyn_cast <cgraph_node *> (ref); + if (cref) + ref = cref->global.inlined_to; + } + + /* If this is a reference from symbol itself and there are no aliases, we + may be sure that the symbol was not interposed by soemthing else because + the symbol itself would be unreachable otherwise. This is important + to optimize recursive functions well. + + This assumption may be broken by inlining: if symbol is interposable + but the body is available (i.e. declared inline), inliner may make + the body reachable even with interposition. */ + if (this == ref && !has_aliases_p () + && (!cnode + || symtab->state >= IPA_SSA_AFTER_INLINING + || get_availability () >= AVAIL_INTERPOSABLE)) + return true; + + + /* References within one comdat group are always bound in a group. */ + if (ref + && symtab->state >= IPA_SSA_AFTER_INLINING + && get_comdat_group () + && get_comdat_group () == ref->get_comdat_group ()) + return true; + + return false; +} diff --git a/gcc/varpool.c b/gcc/varpool.c index cbbdda4..12696a4 100644 --- a/gcc/varpool.c +++ b/gcc/varpool.c @@ -482,7 +482,7 @@ varpool_node::add (tree decl) /* Return variable availability. See cgraph.h for description of individual return values. */ enum availability -varpool_node::get_availability (void) +varpool_node::get_availability (symtab_node *ref) { if (!definition) return AVAIL_NOT_AVAILABLE; @@ -495,9 +495,16 @@ varpool_node::get_availability (void) { enum availability avail; - ultimate_alias_target (&avail); + ultimate_alias_target (&avail, ref); return avail; } + /* If this is a reference from symbol itself and there are no aliases, we + may be sure that the symbol was not interposed by soemthing else because + the symbol itself would be unreachable otherwise. */ + if ((this == ref && !has_aliases_p ()) + || (ref && get_comdat_group () + && get_comdat_group () == ref->get_comdat_group ())) + return AVAIL_AVAILABLE; /* If the variable can be overwritten, return OVERWRITABLE. Takes care of at least one notable extension - the COMDAT variables used to share template instantiations in C++. */ |