re PR c++/70018 (Possible issue around IPO and C++ comdats discovered as pure/const)
authorJan Hubicka <jh@suse.cz>
Thu, 21 Apr 2016 09:05:07 +0000 (11:05 +0200)
committerJan Hubicka <hubicka@gcc.gnu.org>
Thu, 21 Apr 2016 09:05:07 +0000 (09:05 +0000)
PR ipa/70018
* cgraph.c (cgraph_set_nothrow_flag_1): Rename to ...
(set_nothrow_flag_1): ... this; handle interposition correctly;
recurse on aliases and thunks.
(cgraph_node::set_nothrow_flag): New.
* ipa-pure-const.c (ignore_edge_for_nothrow): Ignore calls to
functions compiled with non-call exceptions that binds to current
def.
(propagate_nothrow): Be safe WRT interposition.
* cgraph.h (set_nothrow_flag): Update prototype.

* g++.dg/ipa/nothrow-1.C: New testcase.

From-SVN: r235318

gcc/ChangeLog
gcc/cgraph.c
gcc/cgraph.h
gcc/ipa-pure-const.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/ipa/nothrow-1.C [new file with mode: 0644]

index d24599f7dcc677e2c12146c904cbbd3cb03d3985..d58b38f04497c1136dce2e957ddf34a4aa005e44 100644 (file)
@@ -1,3 +1,16 @@
+2016-04-20  Jan Hubicka  <jh@suse.cz>
+
+       PR ipa/70018
+       * cgraph.c (cgraph_set_nothrow_flag_1): Rename to ...
+       (set_nothrow_flag_1): ... this; handle interposition correctly;
+       recurse on aliases and thunks.
+       (cgraph_node::set_nothrow_flag): New.
+       * ipa-pure-const.c (ignore_edge_for_nothrow): Ignore calls to
+       functions compiled with non-call exceptions that binds to current
+       def.
+       (propagate_nothrow): Be safe WRT interposition.
+       * cgraph.h (set_nothrow_flag): Update prototype.
+
 2016-04-18  Jan Hubicka  <jh@suse.cz>
 
        * tree-ssa-loop-unswitch.c (tree_unswitch_single_loop): Use also
index d8cb52695109ab69df7cd7a0063da8359c9b4d98..d93a93988f15d2c5db06757ce86571c4a11c041a 100644 (file)
@@ -2358,27 +2358,65 @@ cgraph_node::make_local (void)
 
 /* Worker to set nothrow flag.  */
 
-static bool
-cgraph_set_nothrow_flag_1 (cgraph_node *node, void *data)
+static void
+set_nothrow_flag_1 (cgraph_node *node, bool nothrow, bool non_call,
+                   bool *changed)
 {
   cgraph_edge *e;
 
-  TREE_NOTHROW (node->decl) = data != NULL;
-
-  if (data != NULL)
-    for (e = node->callers; e; e = e->next_caller)
-      e->can_throw_external = false;
-  return false;
+  if (nothrow && !TREE_NOTHROW (node->decl))
+    {
+      /* With non-call exceptions we can't say for sure if other function body
+        was not possibly optimized to stil throw.  */
+      if (!non_call || node->binds_to_current_def_p ())
+       {
+         TREE_NOTHROW (node->decl) = true;
+         *changed = true;
+         for (e = node->callers; e; e = e->next_caller)
+           e->can_throw_external = false;
+       }
+    }
+  else if (!nothrow && TREE_NOTHROW (node->decl))
+    {
+      TREE_NOTHROW (node->decl) = false;
+      *changed = true;
+    }
+  ipa_ref *ref;
+  FOR_EACH_ALIAS (node, ref)
+    {
+      cgraph_node *alias = dyn_cast <cgraph_node *> (ref->referring);
+      if (!nothrow || alias->get_availability () > AVAIL_INTERPOSABLE)
+       set_nothrow_flag_1 (alias, nothrow, non_call, changed);
+    }
+  for (cgraph_edge *e = node->callers; e; e = e->next_caller)
+    if (e->caller->thunk.thunk_p
+       && (!nothrow || e->caller->get_availability () > AVAIL_INTERPOSABLE))
+      set_nothrow_flag_1 (e->caller, nothrow, non_call, changed);
 }
 
 /* Set TREE_NOTHROW on NODE's decl and on aliases of NODE
    if any to NOTHROW.  */
 
-void
+bool
 cgraph_node::set_nothrow_flag (bool nothrow)
 {
-  call_for_symbol_thunks_and_aliases (cgraph_set_nothrow_flag_1,
-                                     (void *)(size_t)nothrow, nothrow == true);
+  bool changed = false;
+  bool non_call = opt_for_fn (decl, flag_non_call_exceptions);
+
+  if (!nothrow || get_availability () > AVAIL_INTERPOSABLE)
+    set_nothrow_flag_1 (this, nothrow, non_call, &changed);
+  else
+    {
+      ipa_ref *ref;
+
+      FOR_EACH_ALIAS (this, ref)
+       {
+         cgraph_node *alias = dyn_cast <cgraph_node *> (ref->referring);
+         if (!nothrow || alias->get_availability () > AVAIL_INTERPOSABLE)
+           set_nothrow_flag_1 (alias, nothrow, non_call, &changed);
+       }
+    }
+  return changed;
 }
 
 /* Worker to set_const_flag.  */
@@ -2517,8 +2555,7 @@ cgraph_node::set_const_flag (bool set_const, bool looping)
 
 /* Info used by set_pure_flag_1.  */
 
-struct
-set_pure_flag_info
+struct set_pure_flag_info
 {
   bool pure;
   bool looping;
index 71e31a4f1e3126cf4c0ac31dbe147e39d647d18d..5ce032e1e5745ed00c2dccf621673fe8b589c938 100644 (file)
@@ -1111,7 +1111,7 @@ public:
 
   /* Set TREE_NOTHROW on cgraph_node's decl and on aliases of the node
      if any to NOTHROW.  */
-  void set_nothrow_flag (bool nothrow);
+  bool set_nothrow_flag (bool nothrow);
 
   /* If SET_CONST is true, mark function, aliases and thunks to be ECF_CONST.
     If SET_CONST if false, clear the flag.
index da8fd4d409b36d001a816544e79703c7d4400603..7647a58fb26ede457aa54b4bf5a9396da711df3f 100644 (file)
@@ -1163,7 +1163,10 @@ ignore_edge_for_nothrow (struct cgraph_edge *e)
   enum availability avail;
   cgraph_node *n = e->callee->function_or_virtual_thunk_symbol (&avail,
                                                                e->caller);
-  return (avail <= AVAIL_INTERPOSABLE || TREE_NOTHROW (n->decl));
+  if (avail <= AVAIL_INTERPOSABLE || TREE_NOTHROW (n->decl))
+    return true;
+  return opt_for_fn (e->callee->decl, flag_non_call_exceptions)
+        && !e->callee->binds_to_current_def_p (e->caller);
 }
 
 /* Return true if NODE is self recursive function.
@@ -1589,14 +1592,20 @@ propagate_nothrow (void)
                    continue;
 
                  struct cgraph_node *y = e->callee->
-                                   function_or_virtual_thunk_symbol (&avail,
-                                                                     e->caller);
+                                  function_or_virtual_thunk_symbol (&avail,
+                                                                    e->caller);
 
                  /* We can use info about the callee only if we know it can
-                    not be interposed.  */
+                    not be interposed.
+                    When callee is compiled with non-call exceptions we also
+                    must check that the declaration is bound to current
+                    body as other semantically equivalent body may still
+                    throw.  */
                  if (avail <= AVAIL_INTERPOSABLE
                      || (!TREE_NOTHROW (y->decl)
-                         && get_function_state (y)->can_throw))
+                         && (get_function_state (y)->can_throw
+                             || (opt_for_fn (y->decl, flag_non_call_exceptions)
+                                 && !e->callee->binds_to_current_def_p (w)))))
                    can_throw = true;
                }
              for (ie = w->indirect_calls; ie && !can_throw;
index c767cd59ae7221f20128bb87863621108a1d3463..eb47876726e4ebd407c3946408c455e2b2e8f1dd 100644 (file)
@@ -1,3 +1,8 @@
+2016-04-20  Jan Hubicka  <jh@suse.cz>
+
+       PR ipa/70018
+       * g++.dg/ipa/nothrow-1.C: New testcase.
+
 2016-04-20  Nathan Sidwell  <nathan@acm.org>
 
        PR c++/55635
diff --git a/gcc/testsuite/g++.dg/ipa/nothrow-1.C b/gcc/testsuite/g++.dg/ipa/nothrow-1.C
new file mode 100644 (file)
index 0000000..df2fbae
--- /dev/null
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fnon-call-exceptions -fdump-tree-optimized"  } */
+int *ptr;
+static int barvar;
+
+/* We can not detect A to be const because it may be interposed by unoptimized
+   body.  */
+inline
+__attribute__ ((noinline))
+int a(void)
+{
+  return *ptr == *ptr;
+}
+main()
+{
+  int aa;
+  ptr = &barvar;
+  try {
+    aa=!a();
+  } catch  (...)
+  {
+    return 1;
+  }
+  ptr = 0;
+  return aa;
+}
+/* { dg-final { scan-tree-dump "__cxa_begin_catch"  "optimized"  } } */