]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
ipa-pure-const.c (struct funct_state_d): Add can_free field.
authorJakub Jelinek <jakub@redhat.com>
Fri, 14 Nov 2014 17:46:50 +0000 (18:46 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Fri, 14 Nov 2014 17:46:50 +0000 (18:46 +0100)
* ipa-pure-const.c (struct funct_state_d): Add can_free field.
(varying_state): Add true for can_free.
(check_call): For builtin or internal !nonfreeing_call_p set
local->can_free.
(check_stmt): For asm volatile and asm with "memory" set
local->can_free.
(analyze_function): Clear local->can_free initially, continue
calling check_stmt until all flags are computed, dump can_free
flag.
(pure_const_write_summary): Write can_free flag.
(pure_const_read_summary): Read it back.
(propagate_pure_const): Propagate also can_free flag, set
w->nonfreeing_fn if it is false after propagation.
* cgraph.h (cgraph_node): Add nonfreeing_fn member.
* gimple.c: Include ipa-ref.h, lto-streamer.h and cgraph.h.
(nonfreeing_call_p): Return cgraph nonfreeing_fn flag if set.
Also return true for IFN_ABNORMAL_DISPATCHER.
* cgraph.c (cgraph_node::dump): Dump nonfreeing_fn flag.
* lto-cgraph.c (lto_output_node): Write nonfreeing_fn flag.
(input_overwrite_node): Read it back.

From-SVN: r217582

gcc/ChangeLog
gcc/cgraph.c
gcc/cgraph.h
gcc/gimple.c
gcc/ipa-pure-const.c
gcc/lto-cgraph.c

index e5ff6e9a615ecef186aa134c7c002b9bc4d37de1..c5ea481145833550affbc4814600803d9c95518d 100644 (file)
@@ -1,3 +1,26 @@
+2014-11-14  Jakub Jelinek  <jakub@redhat.com>
+
+       * ipa-pure-const.c (struct funct_state_d): Add can_free field.
+       (varying_state): Add true for can_free.
+       (check_call): For builtin or internal !nonfreeing_call_p set
+       local->can_free.
+       (check_stmt): For asm volatile and asm with "memory" set
+       local->can_free.
+       (analyze_function): Clear local->can_free initially, continue
+       calling check_stmt until all flags are computed, dump can_free
+       flag.
+       (pure_const_write_summary): Write can_free flag.
+       (pure_const_read_summary): Read it back.
+       (propagate_pure_const): Propagate also can_free flag, set
+       w->nonfreeing_fn if it is false after propagation.
+       * cgraph.h (cgraph_node): Add nonfreeing_fn member.
+       * gimple.c: Include ipa-ref.h, lto-streamer.h and cgraph.h.
+       (nonfreeing_call_p): Return cgraph nonfreeing_fn flag if set.
+       Also return true for IFN_ABNORMAL_DISPATCHER.
+       * cgraph.c (cgraph_node::dump): Dump nonfreeing_fn flag.
+       * lto-cgraph.c (lto_output_node): Write nonfreeing_fn flag.
+       (input_overwrite_node): Read it back.
+
 2014-11-14  Jakub Jelinek  <jakub@redhat.com>
            Marek Polacek  <polacek@redhat.com>
 
index 8dcccbf19c6e2f13ae28b9375da767a495908a7b..a66c9c0ea75dcb4eac0d792bb12a82e29431f084 100644 (file)
@@ -1995,6 +1995,8 @@ cgraph_node::dump (FILE *f)
     fprintf (f, " tm_clone");
   if (icf_merged)
     fprintf (f, " icf_merged");
+  if (nonfreeing_fn)
+    fprintf (f, " nonfreeing_fn");
   if (DECL_STATIC_CONSTRUCTOR (decl))
     fprintf (f," static_constructor (priority:%i)", get_init_priority ());
   if (DECL_STATIC_DESTRUCTOR (decl))
index 82222217898f978731e86b4e1d1033c61a9303b2..e9a14c4d49270c7c811ee57204396f41d37894a0 100644 (file)
@@ -1267,6 +1267,10 @@ public:
   /* True when function is clone created for Pointer Bounds Checker
      instrumentation.  */
   unsigned instrumentation_clone : 1;
+  /* True if call to node can't result in a call to free, munmap or
+     other operation that could make previously non-trapping memory
+     accesses trapping.  */
+  unsigned nonfreeing_fn : 1;
 };
 
 /* A cgraph node set is a collection of cgraph nodes.  A cgraph node
index ac753650e182cb985f73f9bb0c8523a6319c53b4..f8459a448cb989b8ba7b541b9888e50f6c426bd5 100644 (file)
@@ -58,6 +58,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "bitmap.h"
 #include "stringpool.h"
 #include "tree-ssanames.h"
+#include "ipa-ref.h"
+#include "lto-streamer.h"
+#include "cgraph.h"
 
 
 /* All the tuples have their operand vector (if present) at the very bottom
@@ -2538,11 +2541,28 @@ nonfreeing_call_p (gimple call)
        default:
          return true;
       }
-  else if (gimple_call_internal_p (call)
-          && gimple_call_flags (call) & ECF_LEAF)
-    return true;
+  else if (gimple_call_internal_p (call))
+    switch (gimple_call_internal_fn (call))
+      {
+      case IFN_ABNORMAL_DISPATCHER:
+        return true;
+      default:
+       if (gimple_call_flags (call) & ECF_LEAF)
+         return true;
+       return false;
+      }
 
-  return false;
+  tree fndecl = gimple_call_fndecl (call);
+  if (!fndecl)
+    return false;
+  struct cgraph_node *n = cgraph_node::get (fndecl);
+  if (!n)
+    return false;
+  enum availability availability;
+  n = n->function_symbol (&availability);
+  if (!n || availability <= AVAIL_INTERPOSABLE)
+    return false;
+  return n->nonfreeing_fn;
 }
 
 /* Callback for walk_stmt_load_store_ops.
index 6f7b32c12bc3311e9b1b487938ff6447b3f738f0..a55288d7ba4301f0525f4509a7d715f45c000778 100644 (file)
@@ -112,11 +112,15 @@ struct funct_state_d
   bool looping;
 
   bool can_throw;
+
+  /* If function can call free, munmap or otherwise make previously
+     non-trapping memory accesses trapping.  */
+  bool can_free;
 };
 
 /* State used when we know nothing about function.  */
 static struct funct_state_d varying_state
-   = { IPA_NEITHER, IPA_NEITHER, true, true, true };
+   = { IPA_NEITHER, IPA_NEITHER, true, true, true, true };
 
 
 typedef struct funct_state_d * funct_state;
@@ -559,6 +563,10 @@ check_call (funct_state local, gimple call, bool ipa)
       enum pure_const_state_e call_state;
       bool call_looping;
 
+      if (gimple_call_builtin_p (call, BUILT_IN_NORMAL)
+         && !nonfreeing_call_p (call))
+       local->can_free = true;
+
       if (special_builtin_state (&call_state, &call_looping, callee_t))
        {
          worse_state (&local->pure_const_state, &local->looping,
@@ -589,6 +597,8 @@ check_call (funct_state local, gimple call, bool ipa)
            break;
          }
     }
+  else if (gimple_call_internal_p (call) && !nonfreeing_call_p (call))
+    local->can_free = true;
 
   /* When not in IPA mode, we can still handle self recursion.  */
   if (!ipa && callee_t
@@ -753,6 +763,7 @@ check_stmt (gimple_stmt_iterator *gsip, funct_state local, bool ipa)
            fprintf (dump_file, "    memory asm clobber is not const/pure\n");
          /* Abandon all hope, ye who enter here. */
          local->pure_const_state = IPA_NEITHER;
+         local->can_free = true;
        }
       if (gimple_asm_volatile_p (stmt))
        {
@@ -760,7 +771,8 @@ check_stmt (gimple_stmt_iterator *gsip, funct_state local, bool ipa)
            fprintf (dump_file, "    volatile is not const/pure\n");
          /* Abandon all hope, ye who enter here. */
          local->pure_const_state = IPA_NEITHER;
-          local->looping = true;
+         local->looping = true;
+         local->can_free = true;
        }
       return;
     default:
@@ -785,6 +797,7 @@ analyze_function (struct cgraph_node *fn, bool ipa)
   l->looping_previously_known = true;
   l->looping = false;
   l->can_throw = false;
+  l->can_free = false;
   state_from_flags (&l->state_previously_known, &l->looping_previously_known,
                    flags_from_decl_or_type (fn->decl),
                    fn->cannot_return_p ());
@@ -815,7 +828,10 @@ analyze_function (struct cgraph_node *fn, bool ipa)
           gsi_next (&gsi))
        {
          check_stmt (&gsi, l, ipa);
-         if (l->pure_const_state == IPA_NEITHER && l->looping && l->can_throw)
+         if (l->pure_const_state == IPA_NEITHER
+             && l->looping
+             && l->can_throw
+             && l->can_free)
            goto end;
        }
     }
@@ -882,6 +898,8 @@ end:
         fprintf (dump_file, "Function is locally const.\n");
       if (l->pure_const_state == IPA_PURE)
         fprintf (dump_file, "Function is locally pure.\n");
+      if (l->can_free)
+       fprintf (dump_file, "Function can locally free.\n");
     }
   return l;
 }
@@ -1021,6 +1039,7 @@ pure_const_write_summary (void)
          bp_pack_value (&bp, fs->looping_previously_known, 1);
          bp_pack_value (&bp, fs->looping, 1);
          bp_pack_value (&bp, fs->can_throw, 1);
+         bp_pack_value (&bp, fs->can_free, 1);
          streamer_write_bitpack (&bp);
        }
     }
@@ -1080,6 +1099,7 @@ pure_const_read_summary (void)
              fs->looping_previously_known = bp_unpack_value (&bp, 1);
              fs->looping = bp_unpack_value (&bp, 1);
              fs->can_throw = bp_unpack_value (&bp, 1);
+             fs->can_free = bp_unpack_value (&bp, 1);
              if (dump_file)
                {
                  int flags = flags_from_decl_or_type (node->decl);
@@ -1102,6 +1122,8 @@ pure_const_read_summary (void)
                    fprintf (dump_file,"  function is previously known looping\n");
                  if (fs->can_throw)
                    fprintf (dump_file,"  function is locally throwing\n");
+                 if (fs->can_free)
+                   fprintf (dump_file,"  function can locally free\n");
                }
            }
 
@@ -1347,6 +1369,33 @@ propagate_pure_const (void)
                 pure_const_names [pure_const_state],
                 looping);
 
+      /* Find the worst state of can_free for any node in the cycle.  */
+      bool can_free = false;
+      w = node;
+      while (w && !can_free)
+       {
+         struct cgraph_edge *e;
+         funct_state w_l = get_function_state (w);
+
+         if (w_l->can_free
+             || w->get_availability () == AVAIL_INTERPOSABLE
+             || w->indirect_calls)
+           can_free = true;
+
+         for (e = w->callees; e && !can_free; e = e->next_callee)
+           {
+             enum availability avail;
+             struct cgraph_node *y = e->callee->function_symbol (&avail);
+
+             if (avail > AVAIL_INTERPOSABLE)
+               can_free = get_function_state (y)->can_free;
+             else
+               can_free = true;
+           }
+         w_info = (struct ipa_dfs_info *) w->aux;
+         w = w_info->next_cycle;
+       }
+
       /* Copy back the region's pure_const_state which is shared by
         all nodes in the region.  */
       w = node;
@@ -1356,6 +1405,12 @@ propagate_pure_const (void)
          enum pure_const_state_e this_state = pure_const_state;
          bool this_looping = looping;
 
+         w_l->can_free = can_free;
+         w->nonfreeing_fn = !can_free;
+         if (!can_free && dump_file)
+           fprintf (dump_file, "Function found not to call free: %s\n",
+                    w->name ());
+
          if (w_l->state_previously_known != IPA_NEITHER
              && this_state > w_l->state_previously_known)
            {
index b29d1b51a3891afe10507dadc87f4ba4dcbe97b8..3ce2367a3bfe8bf88d1f4ca5e684ec31e699f23d 100644 (file)
@@ -562,6 +562,7 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node,
   bp_pack_value (&bp, node->tm_clone, 1);
   bp_pack_value (&bp, node->calls_comdat_local, 1);
   bp_pack_value (&bp, node->icf_merged, 1);
+  bp_pack_value (&bp, node->nonfreeing_fn, 1);
   bp_pack_value (&bp, node->thunk.thunk_p && !boundary_p, 1);
   bp_pack_enum (&bp, ld_plugin_symbol_resolution,
                LDPR_NUM_KNOWN, node->resolution);
@@ -1168,6 +1169,7 @@ input_overwrite_node (struct lto_file_decl_data *file_data,
   node->tm_clone = bp_unpack_value (bp, 1);
   node->calls_comdat_local = bp_unpack_value (bp, 1);
   node->icf_merged = bp_unpack_value (bp, 1);
+  node->nonfreeing_fn = bp_unpack_value (bp, 1);
   node->thunk.thunk_p = bp_unpack_value (bp, 1);
   node->resolution = bp_unpack_enum (bp, ld_plugin_symbol_resolution,
                                     LDPR_NUM_KNOWN);