diff mbox

Fix nothrow discovery WRT interposition

Message ID 20160417193809.GA13877@kam.mff.cuni.cz
State New
Headers show

Commit Message

Jan Hubicka April 17, 2016, 7:38 p.m. UTC
Hi,
this patch fixes symmetric bug to pure-const discovery but with nothrow flag
when -fnon-call-exceptions is used. In this case we probably can not detect
function as nothrow just based on the fact we see no throwing statements because
they were possibly optimized out as can be seen in the attached testcase.

At least I believe so, but I also tend to recall that in Java it is not valid
to optimize out exception or change order of exceptions and I do not know what
Ada says here. With C++ this is GNU extensions, so I am not 100% sure this is
needed. The testcase attached however behave as expected - the memory access is
optimized out and hypothetical other compiler may keep it and throw an
exception.  In this case I do not think I can reproduce it with clang because
as far as I can remember it doesn't support non call exceptions.

Again analysys could be improved if we had a way to figure out if the completely
unoptimized function body had something that can throw in it.

Eric, since Ada is one of main users of non-call exceptions, does this make sense
to you?


	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.
Index: cgraph.c
===================================================================
--- cgraph.c	(revision 235081)
@@ -2358,27 +2363,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 +2560,7 @@ cgraph_node::set_const_flag (bool set_co
 
 /* Info used by set_pure_flag_1.  */
 
-struct
-set_pure_flag_info
+struct set_pure_flag_info
 {
   bool pure;
   bool looping;
diff mbox

Patch

Index: ipa-pure-const.c
===================================================================
--- ipa-pure-const.c	(revision 235065)
+++ ipa-pure-const.c	(working copy)
@@ -1163,7 +1161,10 @@  ignore_edge_for_nothrow (struct cgraph_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 +1595,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: cgraph.h
===================================================================
--- cgraph.h	(revision 235081)
+++ cgraph.h	(working copy)
@@ -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: testsuite/g++.dg/ipa/nothrow-1.C
===================================================================
--- testsuite/g++.dg/ipa/nothrow-1.C	(revision 0)
+++ testsuite/g++.dg/ipa/nothrow-1.C	(working copy)
@@ -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"  } } */