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
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