re PR c++/70018 (Possible issue around IPO and C++ comdats discovered as pure/const)
authorJan Hubicka <jh@suse.cz>
Sun, 17 Apr 2016 16:04:05 +0000 (18:04 +0200)
committerJan Hubicka <hubicka@gcc.gnu.org>
Sun, 17 Apr 2016 16:04:05 +0000 (16:04 +0000)
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

gcc/ChangeLog
gcc/cgraph.c
gcc/cgraph.h

index 5dd1b09..1e9ef9b 100644 (file)
@@ -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
index c43adb9..d8cb526 100644 (file)
@@ -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
index 5b2b4bc..71e31a4 100644 (file)
@@ -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