diff options
author | Jan Hubicka <jh@suse.cz> | 2016-04-17 18:04:05 +0200 |
---|---|---|
committer | Jan Hubicka <hubicka@gcc.gnu.org> | 2016-04-17 16:04:05 +0000 |
commit | 69a4e898c8f508f0c56c72c791a38bc432e9fa69 (patch) | |
tree | dab34f7a272189308734eab631e4f507410d806b | |
parent | b3de2446458bf8c61a8e54d6bd424bafe7ab479e (diff) | |
download | gcc-69a4e898c8f508f0c56c72c791a38bc432e9fa69.zip gcc-69a4e898c8f508f0c56c72c791a38bc432e9fa69.tar.gz gcc-69a4e898c8f508f0c56c72c791a38bc432e9fa69.tar.bz2 |
re PR c++/70018 (Possible issue around IPO and C++ comdats discovered as pure/const)
PR ipa/70018
* cgraph.h (cgraph_node::set_const_flag,
cgraph_node::set_pure_flag): Update prototype to return bool;
update comment.
* cgraph.c (cgraph_node::call_for_symbol_thunks_and_aliases): Thunks
of interposable symbol are interposable, too.
(cgraph_set_const_flag_1): Rename to ...
(set_const_flag_1): ... this one; change to self recursive function
instead of call_for_symbol_thunks_and_aliases. Handle correctly
clearnig the flag in all variants and also virtual thunks of const
functions are pure; track if any change was done.
(cgraph_node::set_const_flag): Update.
(struct set_pure_flag_info): New struct.
(cgraph_set_pure_flag_1): Rename to ...
(set_pure_flag_1): ... this one; take set_pure_flag_info parameter
rather than pointer encoded flags; track if any changes was done;
handle correctly clearning flag and setting flag of aliases already
declared const.
(cgraph_node::set_pure_flag): Update.
(cgraph_node::set_nothrow_flag): Handle correctly clearning the flag.
From-SVN: r235081
-rw-r--r-- | gcc/ChangeLog | 23 | ||||
-rw-r--r-- | gcc/cgraph.c | 222 | ||||
-rw-r--r-- | gcc/cgraph.h | 21 |
3 files changed, 211 insertions, 55 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 5dd1b09..1e9ef9b 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,26 @@ +2016-04-17 Jan Hubicka <jh@suse.cz> + + PR ipa/70018 + * cgraph.h (cgraph_node::set_const_flag, + cgraph_node::set_pure_flag): Update prototype to return bool; + update comment. + * cgraph.c (cgraph_node::call_for_symbol_thunks_and_aliases): Thunks + of interposable symbol are interposable, too. + (cgraph_set_const_flag_1): Rename to ... + (set_const_flag_1): ... this one; change to self recursive function + instead of call_for_symbol_thunks_and_aliases. Handle correctly + clearnig the flag in all variants and also virtual thunks of const + functions are pure; track if any change was done. + (cgraph_node::set_const_flag): Update. + (struct set_pure_flag_info): New struct. + (cgraph_set_pure_flag_1): Rename to ... + (set_pure_flag_1): ... this one; take set_pure_flag_info parameter + rather than pointer encoded flags; track if any changes was done; + handle correctly clearning flag and setting flag of aliases already + declared const. + (cgraph_node::set_pure_flag): Update. + (cgraph_node::set_nothrow_flag): Handle correctly clearning the flag. + 2016-04-17 Tom de Vries <tom@codesourcery.com> PR other/70433 diff --git a/gcc/cgraph.c b/gcc/cgraph.c index c43adb9..d8cb526 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -2308,6 +2308,8 @@ cgraph_node::call_for_symbol_thunks_and_aliases (bool (*callback) exclude_virtual_thunks)) return true; } + if (get_availability () <= AVAIL_INTERPOSABLE) + return false; for (e = callers; e; e = e->next_caller) if (e->caller->thunk.thunk_p && (include_overwritable @@ -2376,95 +2378,215 @@ void cgraph_node::set_nothrow_flag (bool nothrow) { call_for_symbol_thunks_and_aliases (cgraph_set_nothrow_flag_1, - (void *)(size_t)nothrow, false); + (void *)(size_t)nothrow, nothrow == true); } -/* Worker to set const flag. */ +/* Worker to set_const_flag. */ -static bool -cgraph_set_const_flag_1 (cgraph_node *node, void *data) +static void +set_const_flag_1 (cgraph_node *node, bool set_const, bool looping, + bool *changed) { /* Static constructors and destructors without a side effect can be optimized out. */ - if (data && !((size_t)data & 2)) + if (set_const && !looping) { if (DECL_STATIC_CONSTRUCTOR (node->decl)) - DECL_STATIC_CONSTRUCTOR (node->decl) = 0; + { + DECL_STATIC_CONSTRUCTOR (node->decl) = 0; + *changed = true; + } if (DECL_STATIC_DESTRUCTOR (node->decl)) - DECL_STATIC_DESTRUCTOR (node->decl) = 0; + { + DECL_STATIC_DESTRUCTOR (node->decl) = 0; + *changed = true; + } + } + if (!set_const) + { + if (TREE_READONLY (node->decl)) + { + TREE_READONLY (node->decl) = 0; + DECL_LOOPING_CONST_OR_PURE_P (node->decl) = false; + *changed = true; + } } + else + { + /* Consider function: - /* Consider function: + bool a(int *p) + { + return *p==*p; + } - bool a(int *p) - { - return *p==*p; - } + During early optimization we will turn this into: - During early optimization we will turn this into: + bool a(int *p) + { + return true; + } - bool a(int *p) - { - return true; - } + Now if this function will be detected as CONST however when interposed + it may end up being just pure. We always must assume the worst + scenario here. */ + if (TREE_READONLY (node->decl)) + { + if (!looping && DECL_LOOPING_CONST_OR_PURE_P (node->decl)) + { + DECL_LOOPING_CONST_OR_PURE_P (node->decl) = false; + *changed = true; + } + } + else if (node->binds_to_current_def_p ()) + { + TREE_READONLY (node->decl) = true; + DECL_LOOPING_CONST_OR_PURE_P (node->decl) = looping; + DECL_PURE_P (node->decl) = false; + *changed = true; + } + else + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Dropping state to PURE because function does " + "not bind to current def.\n"); + if (!DECL_PURE_P (node->decl)) + { + DECL_PURE_P (node->decl) = true; + DECL_LOOPING_CONST_OR_PURE_P (node->decl) = looping; + *changed = true; + } + else if (!looping && DECL_LOOPING_CONST_OR_PURE_P (node->decl)) + { + DECL_LOOPING_CONST_OR_PURE_P (node->decl) = false; + *changed = true; + } + } + } - Now if this function will be detected as CONST however when interposed it - may end up being just pure. We always must assume the worst scenario here. - */ - if (TREE_READONLY (node->decl)) - ; - else if (node->binds_to_current_def_p ()) - TREE_READONLY (node->decl) = data != NULL; - else + ipa_ref *ref; + FOR_EACH_ALIAS (node, ref) { - if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, "Dropping state to PURE because function does " - "not bind to current def.\n"); - DECL_PURE_P (node->decl) = data != NULL; + cgraph_node *alias = dyn_cast <cgraph_node *> (ref->referring); + if (!set_const || alias->get_availability () > AVAIL_INTERPOSABLE) + set_const_flag_1 (alias, set_const, looping, changed); } - DECL_LOOPING_CONST_OR_PURE_P (node->decl) = ((size_t)data & 2) != 0; - return false; + for (cgraph_edge *e = node->callers; e; e = e->next_caller) + if (e->caller->thunk.thunk_p + && (!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 + || !node->binds_to_current_def_p (e->caller))) + *changed |= e->caller->set_pure_flag (true, looping); + else + set_const_flag_1 (e->caller, set_const, looping, changed); + } } -/* Set TREE_READONLY on cgraph_node's decl and on aliases of the node - if any to READONLY. */ +/* If SET_CONST is true, mark function, aliases and thunks to be ECF_CONST. + If SET_CONST if false, clear the flag. -void -cgraph_node::set_const_flag (bool readonly, bool looping) + When setting the flag be careful about possible interposition and + do not set the flag for functions that can be interposet and set pure + flag for functions that can bind to other definition. + + Return true if any change was done. */ + +bool +cgraph_node::set_const_flag (bool set_const, bool looping) { - call_for_symbol_thunks_and_aliases (cgraph_set_const_flag_1, - (void *)(size_t)(readonly + (int)looping * 2), - false, true); + bool changed = false; + if (!set_const || get_availability () > AVAIL_INTERPOSABLE) + set_const_flag_1 (this, set_const, looping, &changed); + else + { + ipa_ref *ref; + + FOR_EACH_ALIAS (this, ref) + { + cgraph_node *alias = dyn_cast <cgraph_node *> (ref->referring); + if (!set_const || alias->get_availability () > AVAIL_INTERPOSABLE) + set_const_flag_1 (alias, set_const, looping, &changed); + } + } + return changed; } -/* Worker to set pure flag. */ +/* Info used by set_pure_flag_1. */ + +struct +set_pure_flag_info +{ + bool pure; + bool looping; + bool changed; +}; + +/* Worker to set_pure_flag. */ static bool -cgraph_set_pure_flag_1 (cgraph_node *node, void *data) +set_pure_flag_1 (cgraph_node *node, void *data) { + struct set_pure_flag_info *info = (struct set_pure_flag_info *)data; /* Static constructors and destructors without a side effect can be optimized out. */ - if (data && !((size_t)data & 2)) + if (info->pure && !info->looping) { if (DECL_STATIC_CONSTRUCTOR (node->decl)) - DECL_STATIC_CONSTRUCTOR (node->decl) = 0; + { + DECL_STATIC_CONSTRUCTOR (node->decl) = 0; + info->changed = true; + } if (DECL_STATIC_DESTRUCTOR (node->decl)) - DECL_STATIC_DESTRUCTOR (node->decl) = 0; + { + DECL_STATIC_DESTRUCTOR (node->decl) = 0; + info->changed = true; + } + } + if (info->pure) + { + if (!DECL_PURE_P (node->decl) && !TREE_READONLY (node->decl)) + { + DECL_PURE_P (node->decl) = true; + DECL_LOOPING_CONST_OR_PURE_P (node->decl) = info->looping; + info->changed = true; + } + else if (DECL_LOOPING_CONST_OR_PURE_P (node->decl) + && !info->looping) + { + DECL_LOOPING_CONST_OR_PURE_P (node->decl) = false; + info->changed = true; + } + } + else + { + if (DECL_PURE_P (node->decl)) + { + DECL_PURE_P (node->decl) = false; + DECL_LOOPING_CONST_OR_PURE_P (node->decl) = false; + info->changed = true; + } } - DECL_PURE_P (node->decl) = data != NULL; - DECL_LOOPING_CONST_OR_PURE_P (node->decl) = ((size_t)data & 2) != 0; return false; } /* Set DECL_PURE_P on cgraph_node's decl and on aliases of the node - if any to PURE. */ + if any to PURE. -void + When setting the flag, be careful about possible interposition. + Return true if any change was done. */ + +bool cgraph_node::set_pure_flag (bool pure, bool looping) { - call_for_symbol_thunks_and_aliases (cgraph_set_pure_flag_1, - (void *)(size_t)(pure + (int)looping * 2), - false, true); + struct set_pure_flag_info info = {pure, looping, false}; + if (!pure) + looping = false; + call_for_symbol_thunks_and_aliases (set_pure_flag_1, &info, !pure, true); + return info.changed; } /* Return true when cgraph_node can not return or throw and thus diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 5b2b4bc..71e31a4 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -1113,13 +1113,24 @@ public: if any to NOTHROW. */ void set_nothrow_flag (bool nothrow); - /* Set TREE_READONLY on cgraph_node's decl and on aliases of the node - if any to READONLY. */ - void set_const_flag (bool readonly, bool looping); + /* If SET_CONST is true, mark function, aliases and thunks to be ECF_CONST. + If SET_CONST if false, clear the flag. + + When setting the flag be careful about possible interposition and + do not set the flag for functions that can be interposet and set pure + flag for functions that can bind to other definition. + + Return true if any change was done. */ + + bool set_const_flag (bool set_const, bool looping); /* Set DECL_PURE_P on cgraph_node's decl and on aliases of the node - if any to PURE. */ - void set_pure_flag (bool pure, bool looping); + if any to PURE. + + When setting the flag, be careful about possible interposition. + Return true if any change was done. */ + + bool set_pure_flag (bool pure, bool looping); /* Call callback on function and aliases associated to the function. When INCLUDE_OVERWRITABLE is false, overwritable aliases and thunks are |