ipa-pure-const.c (ignore_edge_for_pure_const): New function.
[gcc.git] / gcc / ipa-pure-const.c
index f0373e65d8f53ffd8130821acb38e80a8c663077..4cf09183f3a23b570bbee777b24f8c32afb54a1f 100644 (file)
@@ -35,38 +35,27 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "backend.h"
+#include "target.h"
 #include "tree.h"
 #include "gimple.h"
-#include "hard-reg-set.h"
-#include "alias.h"
-#include "fold-const.h"
-#include "print-tree.h"
+#include "tree-pass.h"
+#include "tree-streamer.h"
+#include "cgraph.h"
+#include "diagnostic.h"
 #include "calls.h"
 #include "cfganal.h"
-#include "internal-fn.h"
 #include "tree-eh.h"
 #include "gimple-iterator.h"
 #include "gimple-walk.h"
 #include "tree-cfg.h"
 #include "tree-ssa-loop-niter.h"
-#include "tree-inline.h"
-#include "tree-pass.h"
 #include "langhooks.h"
-#include "cgraph.h"
 #include "ipa-utils.h"
-#include "flags.h"
-#include "diagnostic.h"
 #include "gimple-pretty-print.h"
-#include "langhooks.h"
-#include "target.h"
-#include "lto-streamer.h"
-#include "data-streamer.h"
-#include "tree-streamer.h"
 #include "cfgloop.h"
 #include "tree-scalar-evolution.h"
 #include "intl.h"
 #include "opts.h"
-#include "varasm.h"
 
 /* Lattice values for const and pure functions.  Everything starts out
    being const, then may drop to pure and then neither depending on
@@ -636,7 +625,7 @@ check_call (funct_state local, gcall *call, bool ipa)
 /* Wrapper around check_decl for loads in local more.  */
 
 static bool
-check_load (gimple, tree op, tree, void *data)
+check_load (gimple *, tree op, tree, void *data)
 {
   if (DECL_P (op))
     check_decl ((funct_state)data, op, false, false);
@@ -648,7 +637,7 @@ check_load (gimple, tree op, tree, void *data)
 /* Wrapper around check_decl for stores in local more.  */
 
 static bool
-check_store (gimple, tree op, tree, void *data)
+check_store (gimple *, tree op, tree, void *data)
 {
   if (DECL_P (op))
     check_decl ((funct_state)data, op, true, false);
@@ -660,7 +649,7 @@ check_store (gimple, tree op, tree, void *data)
 /* Wrapper around check_decl for loads in ipa mode.  */
 
 static bool
-check_ipa_load (gimple, tree op, tree, void *data)
+check_ipa_load (gimple *, tree op, tree, void *data)
 {
   if (DECL_P (op))
     check_decl ((funct_state)data, op, false, true);
@@ -672,7 +661,7 @@ check_ipa_load (gimple, tree op, tree, void *data)
 /* Wrapper around check_decl for stores in ipa mode.  */
 
 static bool
-check_ipa_store (gimple, tree op, tree, void *data)
+check_ipa_store (gimple *, tree op, tree, void *data)
 {
   if (DECL_P (op))
     check_decl ((funct_state)data, op, true, true);
@@ -686,7 +675,7 @@ check_ipa_store (gimple, tree op, tree, void *data)
 static void
 check_stmt (gimple_stmt_iterator *gsip, funct_state local, bool ipa)
 {
-  gimple stmt = gsi_stmt (*gsip);
+  gimple *stmt = gsi_stmt (*gsip);
 
   if (is_gimple_debug (stmt))
     return;
@@ -1135,11 +1124,18 @@ pure_const_read_summary (void)
     }
 }
 
+/* We only propagate across edges that can throw externally and their callee
+   is not interposable.  */
 
 static bool
-ignore_edge (struct cgraph_edge *e)
+ignore_edge_for_nothrow (struct cgraph_edge *e)
 {
-  return (!e->can_throw_external);
+  if (!e->can_throw_external || TREE_NOTHROW (e->callee->decl))
+    return true;
+
+  enum availability avail;
+  cgraph_node *n = e->callee->function_or_virtual_thunk_symbol (&avail);
+  return (avail <= AVAIL_INTERPOSABLE || TREE_NOTHROW (n->decl));
 }
 
 /* Return true if NODE is self recursive function.
@@ -1168,6 +1164,17 @@ cdtor_p (cgraph_node *n, void *)
   return false;
 }
 
+/* We only propagate across edges with non-interposable callee.  */
+
+static bool
+ignore_edge_for_pure_const (struct cgraph_edge *e)
+{
+  enum availability avail;
+  e->callee->function_or_virtual_thunk_symbol (&avail);
+  return (avail <= AVAIL_INTERPOSABLE);
+}
+
+
 /* Produce transitive closure over the callgraph and compute pure/const
    attributes.  */
 
@@ -1183,7 +1190,8 @@ propagate_pure_const (void)
   struct ipa_dfs_info * w_info;
   bool remove_p = false;
 
-  order_pos = ipa_reduced_postorder (order, true, false, NULL);
+  order_pos = ipa_reduced_postorder (order, true, false,
+                                    ignore_edge_for_pure_const);
   if (dump_file)
     {
       cgraph_node::dump_cgraph (dump_file);
@@ -1230,7 +1238,7 @@ propagate_pure_const (void)
          if (pure_const_state == IPA_NEITHER)
            break;
 
-         /* For overwritable nodes we can not assume anything.  */
+         /* For interposable nodes we can not assume anything.  */
          if (w->get_availability () == AVAIL_INTERPOSABLE)
            {
              worse_state (&pure_const_state, &looping,
@@ -1239,7 +1247,7 @@ propagate_pure_const (void)
              if (dump_file && (dump_flags & TDF_DETAILS))
                {
                  fprintf (dump_file,
-                          "    Overwritable. state %s looping %i\n",
+                          "    Interposable. state %s looping %i\n",
                           pure_const_names[w_l->state_previously_known],
                           w_l->looping_previously_known);
                }
@@ -1255,7 +1263,8 @@ propagate_pure_const (void)
            looping = true;
 
          /* Now walk the edges and merge in callee properties.  */
-         for (e = w->callees; e; e = e->next_callee)
+         for (e = w->callees; e && pure_const_state != IPA_NEITHER;
+              e = e->next_callee)
            {
              enum availability avail;
              struct cgraph_node *y = e->callee->
@@ -1313,11 +1322,10 @@ propagate_pure_const (void)
              if (pure_const_state == IPA_NEITHER)
                break;
            }
-         if (pure_const_state == IPA_NEITHER)
-           break;
 
          /* Now process the indirect call.  */
-          for (ie = w->indirect_calls; ie; ie = ie->next_callee)
+          for (ie = w->indirect_calls;
+              ie && pure_const_state != IPA_NEITHER; ie = ie->next_callee)
            {
              enum pure_const_state_e edge_state = IPA_CONST;
              bool edge_looping = false;
@@ -1336,11 +1344,10 @@ propagate_pure_const (void)
              if (pure_const_state == IPA_NEITHER)
                break;
            }
-         if (pure_const_state == IPA_NEITHER)
-           break;
 
          /* And finally all loads and stores.  */
-         for (i = 0; w->iterate_reference (i, ref); i++)
+         for (i = 0; w->iterate_reference (i, ref)
+              && pure_const_state != IPA_NEITHER; i++)
            {
              enum pure_const_state_e ref_state = IPA_CONST;
              bool ref_looping = false;
@@ -1430,7 +1437,8 @@ propagate_pure_const (void)
              && this_state > w_l->state_previously_known)
            {
               this_state = w_l->state_previously_known;
-             this_looping |= w_l->looping_previously_known;
+             if (this_state == IPA_NEITHER)
+               this_looping = w_l->looping_previously_known;
            }
          if (!this_looping && self_recursive_p (w))
            this_looping = true;
@@ -1502,7 +1510,8 @@ propagate_nothrow (void)
   int i;
   struct ipa_dfs_info * w_info;
 
-  order_pos = ipa_reduced_postorder (order, true, false, ignore_edge);
+  order_pos = ipa_reduced_postorder (order, true, false,
+                                    ignore_edge_for_nothrow);
   if (dump_file)
     {
       cgraph_node::dump_cgraph (dump_file);
@@ -1526,32 +1535,38 @@ propagate_nothrow (void)
       while (w && !can_throw)
        {
          struct cgraph_edge *e, *ie;
-         funct_state w_l = get_function_state (w);
-
-         if (w_l->can_throw
-             || w->get_availability () == AVAIL_INTERPOSABLE)
-           can_throw = true;
 
-         for (e = w->callees; e && !can_throw; e = e->next_callee)
+         if (!TREE_NOTHROW (w->decl))
            {
-             enum availability avail;
-             struct cgraph_node *y = e->callee->
-                               function_or_virtual_thunk_symbol (&avail);
+             funct_state w_l = get_function_state (w);
 
-             if (avail > AVAIL_INTERPOSABLE)
+             if (w_l->can_throw
+                 || w->get_availability () == AVAIL_INTERPOSABLE)
+               can_throw = true;
+
+             for (e = w->callees; e && !can_throw; e = e->next_callee)
                {
-                 funct_state y_l = get_function_state (y);
+                 enum availability avail;
+
+                 if (!e->can_throw_external || TREE_NOTHROW (e->callee->decl))
+                   continue;
+
+                 struct cgraph_node *y = e->callee->
+                                   function_or_virtual_thunk_symbol (&avail);
 
-                 if (y_l->can_throw && !TREE_NOTHROW (w->decl)
-                     && e->can_throw_external)
+                 /* We can use info about the callee only if we know it can
+                    not be interposed.  */
+                 if (avail <= AVAIL_INTERPOSABLE
+                     || (!TREE_NOTHROW (y->decl)
+                         && get_function_state (y)->can_throw))
                    can_throw = true;
                }
-             else if (e->can_throw_external && !TREE_NOTHROW (y->decl))
-               can_throw = true;
+             for (ie = w->indirect_calls; ie && !can_throw;
+                  ie = ie->next_callee)
+               if (ie->can_throw_external
+                   && !(ie->indirect_info->ecf_flags & ECF_NOTHROW))
+                 can_throw = true;
            }
-          for (ie = w->indirect_calls; ie && !can_throw; ie = ie->next_callee)
-           if (ie->can_throw_external)
-             can_throw = true;
          w_info = (struct ipa_dfs_info *) w->aux;
          w = w_info->next_cycle;
        }
@@ -1661,7 +1676,7 @@ skip_function_for_local_pure_const (struct cgraph_node *node)
   if (node->get_availability () <= AVAIL_INTERPOSABLE)
     {
       if (dump_file)
-        fprintf (dump_file, "Function is not available or overwritable; not analyzing.\n");
+        fprintf (dump_file, "Function is not available or interposable; not analyzing.\n");
       return true;
     }
   return false;