Extend ipa-pure-const pass to propagate malloc attribute.
authorPrathamesh Kulkarni <prathamesh.kulkarni@linaro.org>
Fri, 27 Oct 2017 10:48:49 +0000 (10:48 +0000)
committerPrathamesh Kulkarni <prathamesh3492@gcc.gnu.org>
Fri, 27 Oct 2017 10:48:49 +0000 (10:48 +0000)
2017-10-27  Prathamesh Kulkarni  <prathamesh.kulkarni@linaro.org>

* cgraph.h (set_malloc_flag): Declare.
* cgraph.c (set_malloc_flag_1): New function.
(set_malloc_flag): Likewise.
* ipa-fnsummary.h (ipa_call_summary): Add new field is_return_callee.
* ipa-fnsummary.c (ipa_call_summary::reset): Set is_return_callee to
false.
(read_ipa_call_summary): Add support for reading is_return_callee.
(write_ipa_call_summary): Stream is_return_callee.
* ipa-inline.c (ipa_inline): Remove call to ipa_free_fn_summary.
* ipa-pure-const.c: Add headers ssa.h, alloc-pool.h, symbol-summary.h,
ipa-prop.h, ipa-fnsummary.h.
(pure_const_names): Change to static.
(malloc_state_e): Define.
(malloc_state_names): Define.
(funct_state_d): Add field malloc_state.
(varying_state): Set malloc_state to STATE_MALLOC_BOTTOM.
(check_retval_uses): New function.
(malloc_candidate_p): Likewise.
(analyze_function): Add support for malloc attribute.
(pure_const_write_summary): Stream malloc_state.
(pure_const_read_summary): Add support for reading malloc_state.
(dump_malloc_lattice): New function.
(propagate_malloc): New function.
(warn_function_malloc): New function.
(ipa_pure_const::execute): Call propagate_malloc and
ipa_free_fn_summary.
(pass_local_pure_const::execute): Add support for malloc attribute.
* ssa-iterators.h (RETURN_FROM_IMM_USE_STMT): New macro.
* doc/invoke.texi: Document Wsuggest-attribute=malloc.

testsuite/
* gcc.dg/ipa/propmalloc-1.c: New test-case.
* gcc.dg/ipa/propmalloc-2.c: Likewise.
* gcc.dg/ipa/propmalloc-3.c: Likewise.

From-SVN: r254140

14 files changed:
gcc/ChangeLog
gcc/cgraph.c
gcc/cgraph.h
gcc/common.opt
gcc/doc/invoke.texi
gcc/ipa-fnsummary.c
gcc/ipa-fnsummary.h
gcc/ipa-inline.c
gcc/ipa-pure-const.c
gcc/ssa-iterators.h
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/ipa/propmalloc-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/ipa/propmalloc-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/ipa/propmalloc-3.c [new file with mode: 0644]

index 02335006e025374836cbbc138ed4c5516aa6d154..159cae7f3540e05504f53e62332d395a9375468a 100644 (file)
@@ -1,3 +1,35 @@
+2017-10-27  Prathamesh Kulkarni  <prathamesh.kulkarni@linaro.org>
+
+       * cgraph.h (set_malloc_flag): Declare.
+       * cgraph.c (set_malloc_flag_1): New function.
+       (set_malloc_flag): Likewise.
+       * ipa-fnsummary.h (ipa_call_summary): Add new field is_return_callee.
+       * ipa-fnsummary.c (ipa_call_summary::reset): Set is_return_callee to
+       false.
+       (read_ipa_call_summary): Add support for reading is_return_callee.
+       (write_ipa_call_summary): Stream is_return_callee.
+       * ipa-inline.c (ipa_inline): Remove call to ipa_free_fn_summary.
+       * ipa-pure-const.c: Add headers ssa.h, alloc-pool.h, symbol-summary.h,
+       ipa-prop.h, ipa-fnsummary.h.
+       (pure_const_names): Change to static.
+       (malloc_state_e): Define.
+       (malloc_state_names): Define.
+       (funct_state_d): Add field malloc_state.
+       (varying_state): Set malloc_state to STATE_MALLOC_BOTTOM.
+       (check_retval_uses): New function.
+       (malloc_candidate_p): Likewise.
+       (analyze_function): Add support for malloc attribute.
+       (pure_const_write_summary): Stream malloc_state.
+       (pure_const_read_summary): Add support for reading malloc_state.
+       (dump_malloc_lattice): New function.
+       (propagate_malloc): New function.
+       (warn_function_malloc): New function.
+       (ipa_pure_const::execute): Call propagate_malloc and
+       ipa_free_fn_summary.
+       (pass_local_pure_const::execute): Add support for malloc attribute.
+       * ssa-iterators.h (RETURN_FROM_IMM_USE_STMT): New macro.
+       * doc/invoke.texi: Document Wsuggest-attribute=malloc.
+
 2017-10-27  Martin Liska  <mliska@suse.cz>
 
        PR gcov-profile/82457
index d8da3dd76cdffe783975785ed1b25abd3d6ab8c5..b79f9e837ed27bbac40f46fa72d1602ef9a3c101 100644 (file)
@@ -2530,6 +2530,53 @@ cgraph_node::set_nothrow_flag (bool nothrow)
   return changed;
 }
 
+/* Worker to set malloc flag.  */
+static void
+set_malloc_flag_1 (cgraph_node *node, bool malloc_p, bool *changed)
+{
+  if (malloc_p && !DECL_IS_MALLOC (node->decl))
+    {
+      DECL_IS_MALLOC (node->decl) = true;
+      *changed = true;
+    }
+
+  ipa_ref *ref;
+  FOR_EACH_ALIAS (node, ref)
+    {
+      cgraph_node *alias = dyn_cast<cgraph_node *> (ref->referring);
+      if (!malloc_p || alias->get_availability () > AVAIL_INTERPOSABLE)
+       set_malloc_flag_1 (alias, malloc_p, changed);
+    }
+
+  for (cgraph_edge *e = node->callers; e; e = e->next_caller)
+    if (e->caller->thunk.thunk_p
+       && (!malloc_p || e->caller->get_availability () > AVAIL_INTERPOSABLE))
+      set_malloc_flag_1 (e->caller, malloc_p, changed);
+}
+
+/* Set DECL_IS_MALLOC on NODE's decl and on NODE's aliases if any.  */
+
+bool
+cgraph_node::set_malloc_flag (bool malloc_p)
+{
+  bool changed = false;
+
+  if (!malloc_p || get_availability () > AVAIL_INTERPOSABLE)
+    set_malloc_flag_1 (this, malloc_p, &changed);
+  else
+    {
+      ipa_ref *ref;
+
+      FOR_EACH_ALIAS (this, ref)
+       {
+         cgraph_node *alias = dyn_cast<cgraph_node *> (ref->referring);
+         if (!malloc_p || alias->get_availability () > AVAIL_INTERPOSABLE)
+           set_malloc_flag_1 (alias, malloc_p, &changed);
+       }
+    }
+  return changed;
+}
+
 /* Worker to set_const_flag.  */
 
 static void
index 1758e8b08c17fc217d703aded9a2bc584548cec7..84824e9f814c5bf5c9ad5de393fe6a079d959bb8 100644 (file)
@@ -1151,6 +1151,10 @@ public:
      if any to NOTHROW.  */
   bool set_nothrow_flag (bool nothrow);
 
+  /* SET DECL_IS_MALLOC on cgraph_node's decl and on aliases of the node
+     if any.  */
+  bool set_malloc_flag (bool malloc_p);
+
   /* If SET_CONST is true, mark function, aliases and thunks to be ECF_CONST.
     If SET_CONST if false, clear the flag.
 
index 836f05b95a219d17614f35e8dad61b22fef7d748..d5e831d6b4c560c24bcf03ed29cfa0acb874b5cb 100644 (file)
@@ -741,6 +741,10 @@ Wsuggest-attribute=noreturn
 Common Var(warn_suggest_attribute_noreturn) Warning
 Warn about functions which might be candidates for __attribute__((noreturn)).
 
+Wsuggest-attribute=malloc
+Common Var(warn_suggest_attribute_malloc) Warning
+Warn about functions which might be candidates for __attribute__((malloc)).
+
 Wsuggest-final-types
 Common Var(warn_suggest_final_types) Warning
 Warn about C++ polymorphic types where adding final keyword would improve code quality.
index 87fb9c8dfc17ab75e1c2c3020a03cf3d161f03d3..91b05402ecc347131d2e17b0c182ffe09ccc0861 100644 (file)
@@ -315,7 +315,7 @@ Objective-C and Objective-C++ Dialects}.
 -Wstack-protector  -Wstack-usage=@var{len}  -Wstrict-aliasing @gol
 -Wstrict-aliasing=n  -Wstrict-overflow  -Wstrict-overflow=@var{n} @gol
 -Wstringop-overflow=@var{n} @gol
--Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{]} @gol
+-Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}malloc@r{]} @gol
 -Wsuggest-final-types @gol  -Wsuggest-final-methods  -Wsuggest-override @gol
 -Wmissing-format-attribute  -Wsubobject-linkage @gol
 -Wswitch  -Wswitch-bool  -Wswitch-default  -Wswitch-enum @gol
@@ -5204,7 +5204,7 @@ whether to issue a warning.  Similarly to @option{-Wstringop-overflow=3} this
 setting of the option may result in warnings for benign code.
 @end table
 
-@item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}cold@r{]}
+@item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}cold@r{|}malloc@r{]}
 @opindex Wsuggest-attribute=
 @opindex Wno-suggest-attribute=
 Warn for cases where adding an attribute may be beneficial. The
@@ -5214,21 +5214,25 @@ attributes currently supported are listed below.
 @item -Wsuggest-attribute=pure
 @itemx -Wsuggest-attribute=const
 @itemx -Wsuggest-attribute=noreturn
+@itemx -Wsuggest-attribute=malloc
 @opindex Wsuggest-attribute=pure
 @opindex Wno-suggest-attribute=pure
 @opindex Wsuggest-attribute=const
 @opindex Wno-suggest-attribute=const
 @opindex Wsuggest-attribute=noreturn
 @opindex Wno-suggest-attribute=noreturn
+@opindex Wsuggest-attribute=malloc
+@opindex Wno-suggest-attribute=malloc
 
 Warn about functions that might be candidates for attributes
-@code{pure}, @code{const} or @code{noreturn}.  The compiler only warns for
-functions visible in other compilation units or (in the case of @code{pure} and
-@code{const}) if it cannot prove that the function returns normally. A function
-returns normally if it doesn't contain an infinite loop or return abnormally
-by throwing, calling @code{abort} or trapping.  This analysis requires option
-@option{-fipa-pure-const}, which is enabled by default at @option{-O} and
-higher.  Higher optimization levels improve the accuracy of the analysis.
+@code{pure}, @code{const} or @code{noreturn} or @code{malloc}. The compiler
+only warns for functions visible in other compilation units or (in the case of
+@code{pure} and @code{const}) if it cannot prove that the function returns
+normally. A function returns normally if it doesn't contain an infinite loop or
+return abnormally by throwing, calling @code{abort} or trapping.  This analysis
+requires option @option{-fipa-pure-const}, which is enabled by default at
+@option{-O} and higher.  Higher optimization levels improve the accuracy
+of the analysis.
 
 @item -Wsuggest-attribute=format
 @itemx -Wmissing-format-attribute
index 076ccd40bd7d74817f5bbb02408d0c7073c1a319..f71338e424e3213e18a61937ea01b1705c4e3b71 100644 (file)
@@ -542,6 +542,7 @@ void
 ipa_call_summary::reset ()
 {
   call_stmt_size = call_stmt_time = 0;
+  is_return_callee_uncaptured = false;
   if (predicate)
     edge_predicate_pool.remove (predicate);
   predicate = NULL;
@@ -3204,6 +3205,10 @@ read_ipa_call_summary (struct lto_input_block *ib, struct cgraph_edge *e)
   es->call_stmt_size = streamer_read_uhwi (ib);
   es->call_stmt_time = streamer_read_uhwi (ib);
   es->loop_depth = streamer_read_uhwi (ib);
+
+  bitpack_d bp = streamer_read_bitpack (ib);
+  es->is_return_callee_uncaptured = bp_unpack_value (&bp, 1);
+
   p.stream_in (ib);
   edge_set_predicate (e, &p);
   length = streamer_read_uhwi (ib);
@@ -3360,6 +3365,11 @@ write_ipa_call_summary (struct output_block *ob, struct cgraph_edge *e)
   streamer_write_uhwi (ob, es->call_stmt_size);
   streamer_write_uhwi (ob, es->call_stmt_time);
   streamer_write_uhwi (ob, es->loop_depth);
+
+  bitpack_d bp = bitpack_create (ob->main_stream);
+  bp_pack_value (&bp, es->is_return_callee_uncaptured, 1);
+  streamer_write_bitpack (&bp);
+
   if (es->predicate)
     es->predicate->stream_out (ob);
   else
index f50d6806e61a92add83e10e35f006be18f8d68fe..a794bd09318f136a76507fccd3161a8712c1c703 100644 (file)
@@ -197,7 +197,9 @@ struct ipa_call_summary
   int call_stmt_time;
   /* Depth of loop nest, 0 means no nesting.  */
   unsigned int loop_depth;
-  
+  /* Indicates whether the caller returns the value of it's callee.  */
+  bool is_return_callee_uncaptured;
+
   /* Keep all field empty so summary dumping works during its computation.
      This is useful for debugging.  */
   ipa_call_summary ()
index dd46cb613624c5eb7de0a1313a707ec19428eef9..b8e65e2fa7ec982b19b9dbd750f3b9035df542ee 100644 (file)
@@ -2526,9 +2526,6 @@ ipa_inline (void)
 
   if (dump_file)
     ipa_dump_fn_summaries (dump_file);
-  /* In WPA we use inline summaries for partitioning process.  */
-  if (!flag_wpa)
-    ipa_free_fn_summary ();
   return remove_functions ? TODO_remove_functions : 0;
 }
 
index 3c06e2d303328343be557e66a0f9dc32098fbef7..bdc752207b13de347f1be89d35d973084b84d91d 100644 (file)
@@ -56,6 +56,11 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-scalar-evolution.h"
 #include "intl.h"
 #include "opts.h"
+#include "ssa.h"
+#include "alloc-pool.h"
+#include "symbol-summary.h"
+#include "ipa-prop.h"
+#include "ipa-fnsummary.h"
 
 /* Lattice values for const and pure functions.  Everything starts out
    being const, then may drop to pure and then neither depending on
@@ -67,7 +72,16 @@ enum pure_const_state_e
   IPA_NEITHER
 };
 
-const char *pure_const_names[3] = {"const", "pure", "neither"};
+static const char *pure_const_names[3] = {"const", "pure", "neither"};
+
+enum malloc_state_e
+{
+  STATE_MALLOC_TOP,
+  STATE_MALLOC,
+  STATE_MALLOC_BOTTOM
+};
+
+static const char *malloc_state_names[] = {"malloc_top", "malloc", "malloc_bottom"};
 
 /* Holder for the const_state.  There is one of these per function
    decl.  */
@@ -92,11 +106,13 @@ struct funct_state_d
   /* If function can call free, munmap or otherwise make previously
      non-trapping memory accesses trapping.  */
   bool can_free;
+
+  enum malloc_state_e malloc_state;
 };
 
 /* State used when we know nothing about function.  */
 static struct funct_state_d varying_state
-   = { IPA_NEITHER, IPA_NEITHER, true, true, true, true };
+   = { IPA_NEITHER, IPA_NEITHER, true, true, true, true, STATE_MALLOC_BOTTOM };
 
 
 typedef struct funct_state_d * funct_state;
@@ -216,6 +232,19 @@ warn_function_const (tree decl, bool known_finite)
                         known_finite, warned_about, "const");
 }
 
+/* Emit suggestion about __attribute__((malloc)) for DECL.  */
+
+static void
+warn_function_malloc (tree decl)
+{
+  static hash_set<tree> *warned_about;
+  warned_about
+    = suggest_attribute (OPT_Wsuggest_attribute_malloc, decl,
+                        false, warned_about, "malloc");
+}
+
+/* Emit suggestion about __attribute__((noreturn)) for DECL.  */
+
 static void
 warn_function_noreturn (tree decl)
 {
@@ -827,6 +856,149 @@ check_stmt (gimple_stmt_iterator *gsip, funct_state local, bool ipa)
     }
 }
 
+/* Check that RETVAL is used only in STMT and in comparisons against 0.
+   RETVAL is return value of the function and STMT is return stmt.  */
+
+static bool
+check_retval_uses (tree retval, gimple *stmt)
+{
+  imm_use_iterator use_iter;
+  gimple *use_stmt;
+
+  FOR_EACH_IMM_USE_STMT (use_stmt, use_iter, retval)
+    if (gcond *cond = dyn_cast<gcond *> (use_stmt))
+      {
+       tree op2 = gimple_cond_rhs (cond);
+       if (!integer_zerop (op2))
+         RETURN_FROM_IMM_USE_STMT (use_iter, false);
+      }
+    else if (gassign *ga = dyn_cast<gassign *> (use_stmt))
+      {
+       enum tree_code code = gimple_assign_rhs_code (ga);
+       if (TREE_CODE_CLASS (code) != tcc_comparison)
+         RETURN_FROM_IMM_USE_STMT (use_iter, false);
+       if (!integer_zerop (gimple_assign_rhs2 (ga)))
+         RETURN_FROM_IMM_USE_STMT (use_iter, false);
+      }
+    else if (is_gimple_debug (use_stmt))
+      ;
+    else if (use_stmt != stmt)
+      RETURN_FROM_IMM_USE_STMT (use_iter, false);
+
+  return true;
+}
+
+/* malloc_candidate_p() checks if FUN can possibly be annotated with malloc
+   attribute. Currently this function does a very conservative analysis.
+   FUN is considered to be a candidate if
+   1) It returns a value of pointer type.
+   2) SSA_NAME_DEF_STMT (return_value) is either a function call or
+      a phi, and element of phi is either NULL or
+      SSA_NAME_DEF_STMT(element) is function call.
+   3) The return-value has immediate uses only within comparisons (gcond or gassign)
+      and return_stmt (and likewise a phi arg has immediate use only within comparison
+      or the phi stmt).  */
+
+static bool
+malloc_candidate_p (function *fun, bool ipa)
+{
+  basic_block exit_block = EXIT_BLOCK_PTR_FOR_FN (fun);
+  edge e;
+  edge_iterator ei;
+  cgraph_node *node = cgraph_node::get_create (fun->decl);
+
+#define DUMP_AND_RETURN(reason)  \
+{  \
+  if (dump_file && (dump_flags & TDF_DETAILS))  \
+    fprintf (dump_file, "%s", (reason));  \
+  return false;  \
+}
+
+  if (EDGE_COUNT (exit_block->preds) == 0)
+    return false;
+
+  FOR_EACH_EDGE (e, ei, exit_block->preds)
+    {
+      gimple_stmt_iterator gsi = gsi_last_bb (e->src);
+      greturn *ret_stmt = dyn_cast<greturn *> (gsi_stmt (gsi));
+
+      if (!ret_stmt)
+       return false;
+
+      tree retval = gimple_return_retval (ret_stmt);
+      if (!retval)
+       DUMP_AND_RETURN("No return value.")
+
+      if (TREE_CODE (retval) != SSA_NAME
+         || TREE_CODE (TREE_TYPE (retval)) != POINTER_TYPE)
+       DUMP_AND_RETURN("Return value is not SSA_NAME or not a pointer type.")
+
+      if (!check_retval_uses (retval, ret_stmt))
+       DUMP_AND_RETURN("Return value has uses outside return stmt"
+                       " and comparisons against 0.")
+
+      gimple *def = SSA_NAME_DEF_STMT (retval);
+      if (gcall *call_stmt = dyn_cast<gcall *> (def))
+       {
+         tree callee_decl = gimple_call_fndecl (call_stmt);
+         if (!callee_decl)
+           return false;
+
+         if (!ipa && !DECL_IS_MALLOC (callee_decl))
+           DUMP_AND_RETURN("callee_decl does not have malloc attribute for"
+                           " non-ipa mode.")
+
+         cgraph_edge *cs = node->get_edge (call_stmt);
+         if (cs)
+           {
+             ipa_call_summary *es = ipa_call_summaries->get (cs);
+             gcc_assert (es);
+             es->is_return_callee_uncaptured = true;
+           }
+       }
+
+      else if (gphi *phi = dyn_cast<gphi *> (def))
+       for (unsigned i = 0; i < gimple_phi_num_args (phi); ++i)
+         {
+           tree arg = gimple_phi_arg_def (phi, i);
+           if (TREE_CODE (arg) != SSA_NAME)
+             DUMP_AND_RETURN("phi arg is not SSA_NAME.")
+           if (!(arg == null_pointer_node || check_retval_uses (arg, phi)))
+             DUMP_AND_RETURN("phi arg has uses outside phi"
+                             " and comparisons against 0.")
+
+           gimple *arg_def = SSA_NAME_DEF_STMT (arg);
+           gcall *call_stmt = dyn_cast<gcall *> (arg_def);
+           if (!call_stmt)
+             return false;
+           tree callee_decl = gimple_call_fndecl (call_stmt);
+           if (!callee_decl)
+             return false;
+           if (!ipa && !DECL_IS_MALLOC (callee_decl))
+             DUMP_AND_RETURN("callee_decl does not have malloc attribute for"
+                             " non-ipa mode.")
+
+           cgraph_edge *cs = node->get_edge (call_stmt);
+           if (cs)
+             {
+               ipa_call_summary *es = ipa_call_summaries->get (cs);
+               gcc_assert (es);
+               es->is_return_callee_uncaptured = true;
+             }
+         }
+
+      else
+       DUMP_AND_RETURN("def_stmt of return value is not a call or phi-stmt.")
+    }
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "\nFound %s to be candidate for malloc attribute\n",
+            IDENTIFIER_POINTER (DECL_NAME (fun->decl)));
+  return true;
+
+#undef DUMP_AND_RETURN
+}
+
 
 /* This is the main routine for finding the reference patterns for
    global variables within a function FN.  */
@@ -936,6 +1108,14 @@ end:
   if (TREE_NOTHROW (decl))
     l->can_throw = false;
 
+  l->malloc_state = STATE_MALLOC_BOTTOM;
+  if (DECL_IS_MALLOC (decl))
+    l->malloc_state = STATE_MALLOC;
+  else if (ipa && malloc_candidate_p (DECL_STRUCT_FUNCTION (decl), true))
+    l->malloc_state = STATE_MALLOC_TOP;
+  else if (malloc_candidate_p (DECL_STRUCT_FUNCTION (decl), false))
+    l->malloc_state = STATE_MALLOC;
+
   pop_cfun ();
   if (dump_file)
     {
@@ -949,6 +1129,8 @@ end:
         fprintf (dump_file, "Function is locally pure.\n");
       if (l->can_free)
        fprintf (dump_file, "Function can locally free.\n");
+      if (l->malloc_state == STATE_MALLOC)
+       fprintf (dump_file, "Function is locally malloc.\n");
     }
   return l;
 }
@@ -1082,6 +1264,7 @@ pure_const_write_summary (void)
          bp_pack_value (&bp, fs->looping, 1);
          bp_pack_value (&bp, fs->can_throw, 1);
          bp_pack_value (&bp, fs->can_free, 1);
+         bp_pack_value (&bp, fs->malloc_state, 2);
          streamer_write_bitpack (&bp);
        }
     }
@@ -1142,6 +1325,9 @@ pure_const_read_summary (void)
              fs->looping = bp_unpack_value (&bp, 1);
              fs->can_throw = bp_unpack_value (&bp, 1);
              fs->can_free = bp_unpack_value (&bp, 1);
+             fs->malloc_state
+                       = (enum malloc_state_e) bp_unpack_value (&bp, 2);
+
              if (dump_file)
                {
                  int flags = flags_from_decl_or_type (node->decl);
@@ -1164,6 +1350,8 @@ pure_const_read_summary (void)
                    fprintf (dump_file,"  function is locally throwing\n");
                  if (fs->can_free)
                    fprintf (dump_file,"  function can locally free\n");
+                 fprintf (dump_file, "\n malloc state: %s\n",
+                          malloc_state_names[fs->malloc_state]);
                }
            }
 
@@ -1674,6 +1862,131 @@ propagate_nothrow (void)
   free (order);
 }
 
+/* Debugging function to dump state of malloc lattice.  */
+
+DEBUG_FUNCTION
+static void
+dump_malloc_lattice (FILE *dump_file, const char *s)
+{
+  if (!dump_file)
+    return;
+
+  fprintf (dump_file, "\n\nMALLOC LATTICE %s:\n", s);
+  cgraph_node *node;
+  FOR_EACH_FUNCTION (node)
+    {
+      funct_state fs = get_function_state (node);
+      malloc_state_e state = fs->malloc_state;
+      fprintf (dump_file, "%s: %s\n", node->name (), malloc_state_names[state]);
+    }
+}
+
+/* Propagate malloc attribute across the callgraph.  */
+
+static void
+propagate_malloc (void)
+{
+  cgraph_node *node;
+  FOR_EACH_FUNCTION (node)
+    {
+      if (DECL_IS_MALLOC (node->decl))
+       if (!has_function_state (node))
+         {
+           funct_state l = XCNEW (struct funct_state_d);
+           *l = varying_state;
+           l->malloc_state = STATE_MALLOC;
+           set_function_state (node, l);
+         }
+    }
+
+  dump_malloc_lattice (dump_file, "Initial");
+  struct cgraph_node **order
+    = XNEWVEC (struct cgraph_node *, symtab->cgraph_count);
+  int order_pos = ipa_reverse_postorder (order);
+  bool changed = true;
+
+  while (changed)
+    {
+      changed = false;
+      /* Walk in postorder.  */
+      for (int i = order_pos - 1; i >= 0; --i)
+       {
+         cgraph_node *node = order[i];
+         if (node->alias
+             || !node->definition
+             || !has_function_state (node))
+           continue;
+
+         funct_state l = get_function_state (node);
+
+         /* FIXME: add support for indirect-calls.  */
+         if (node->indirect_calls)
+           {
+             l->malloc_state = STATE_MALLOC_BOTTOM;
+             continue;
+           }
+
+         if (node->get_availability () <= AVAIL_INTERPOSABLE)
+           {
+             l->malloc_state = STATE_MALLOC_BOTTOM;
+             continue;
+           }
+
+         if (l->malloc_state == STATE_MALLOC_BOTTOM)
+           continue;
+
+         vec<cgraph_node *> callees = vNULL;
+         for (cgraph_edge *cs = node->callees; cs; cs = cs->next_callee)
+           {
+             ipa_call_summary *es = ipa_call_summaries->get (cs);
+             if (es && es->is_return_callee_uncaptured)
+               callees.safe_push (cs->callee);
+           }
+
+         malloc_state_e new_state = l->malloc_state;
+         for (unsigned j = 0; j < callees.length (); j++)
+           {
+             cgraph_node *callee = callees[j];
+             if (!has_function_state (callee))
+               {
+                 new_state = STATE_MALLOC_BOTTOM;
+                 break;
+               }
+             malloc_state_e callee_state = get_function_state (callee)->malloc_state;
+             if (new_state < callee_state)
+               new_state = callee_state;
+           }
+         if (new_state != l->malloc_state)
+           {
+             changed = true;
+             l->malloc_state = new_state;
+           }
+       }
+    }
+
+  FOR_EACH_DEFINED_FUNCTION (node)
+    if (has_function_state (node))
+      {
+       funct_state l = get_function_state (node);
+       if (!node->alias
+           && l->malloc_state == STATE_MALLOC
+           && !node->global.inlined_to)
+         {
+           if (dump_file && (dump_flags & TDF_DETAILS))
+             fprintf (dump_file, "Function %s found to be malloc\n",
+                      node->name ());
+
+           bool malloc_decl_p = DECL_IS_MALLOC (node->decl);
+           node->set_malloc_flag (true);
+           if (!malloc_decl_p && warn_suggest_attribute_malloc)
+               warn_function_malloc (node->decl);
+         }
+      }
+
+  dump_malloc_lattice (dump_file, "after propagation");
+  ipa_free_postorder_info ();
+  free (order);
+}
 
 /* Produce the global information by preforming a transitive closure
    on the local information that was produced by generate_summary.  */
@@ -1692,6 +2005,7 @@ execute (function *)
   /* Nothrow makes more function to not lead to return and improve
      later analysis.  */
   propagate_nothrow ();
+  propagate_malloc ();
   remove_p = propagate_pure_const ();
 
   /* Cleanup. */
@@ -1699,6 +2013,10 @@ execute (function *)
     if (has_function_state (node))
       free (get_function_state (node));
   funct_state_vec.release ();
+
+  /* In WPA we use inline summaries for partitioning process.  */
+  if (!flag_wpa)
+    ipa_free_fn_summary ();
   return remove_p ? TODO_remove_functions : 0;
 }
 
@@ -1893,6 +2211,19 @@ pass_local_pure_const::execute (function *fun)
        fprintf (dump_file, "Function found to be nothrow: %s\n",
                 current_function_name ());
     }
+
+  if (l->malloc_state == STATE_MALLOC
+      && !DECL_IS_MALLOC (current_function_decl))
+    {
+      node->set_malloc_flag (true);
+      if (warn_suggest_attribute_malloc)
+       warn_function_malloc (node->decl);
+      changed = true;
+      if (dump_file)
+       fprintf (dump_file, "Function found to be malloc: %s\n",
+                node->name ());
+    }
+
   free (l);
   if (changed)
     return execute_fixup_cfg ();
index c8aa77bd4f34016fe1b1bcd141acf5e7b9d519ba..740cbf13cb2b5d626af308bfa11830bd9386f7a2 100644 (file)
@@ -93,6 +93,12 @@ struct imm_use_iterator
      break;                                                    \
    }
 
+/* Similarly for return.  */
+#define RETURN_FROM_IMM_USE_STMT(ITER, VAL)                    \
+  {                                                            \
+    end_imm_use_stmt_traverse (&(ITER));                       \
+    return (VAL);                                              \
+  }
 
 /* Use this iterator in combination with FOR_EACH_IMM_USE_STMT to
    get access to each occurrence of ssavar on the stmt returned by
index 0b59449911c8b141793608648009b725b6070db9..bdbe863dd9c4763c41e645849e10fc8bcaefd615 100644 (file)
@@ -1,3 +1,9 @@
+2017-10-27  Prathamesh Kulkarni  <prathamesh.kulkarni@linaro.org>
+
+       * gcc.dg/ipa/propmalloc-1.c: New test-case.
+       * gcc.dg/ipa/propmalloc-2.c: Likewise.
+       * gcc.dg/ipa/propmalloc-3.c: Likewise.
+
 2017-10-27  Paolo Carlini  <paolo.carlini@oracle.com>
 
        PR c++/71385
diff --git a/gcc/testsuite/gcc.dg/ipa/propmalloc-1.c b/gcc/testsuite/gcc.dg/ipa/propmalloc-1.c
new file mode 100644 (file)
index 0000000..9a95f81
--- /dev/null
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-pure-const-details" } */
+
+__attribute__((noinline, no_icf, used))
+static void *f(__SIZE_TYPE__ n)
+{
+  void *p = __builtin_malloc (n);
+  if (p == 0)
+    __builtin_abort ();
+  return p;
+}
+
+__attribute__((noinline, no_icf, used))
+static void *bar(__SIZE_TYPE__ n)
+{
+  void *p = f (n);
+  return p;
+}
+
+/* { dg-final { scan-ipa-dump "Function f found to be malloc" "pure-const" } } */
+/* { dg-final { scan-ipa-dump "Function bar found to be malloc" "pure-const" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/propmalloc-2.c b/gcc/testsuite/gcc.dg/ipa/propmalloc-2.c
new file mode 100644 (file)
index 0000000..95b2fd7
--- /dev/null
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-pure-const-details" } */
+
+__attribute__((noinline, used, no_icf))
+static void *foo (__SIZE_TYPE__ n)
+{
+  return __builtin_malloc (n * 10);
+}
+
+__attribute__((noinline, used, no_icf))
+static void *bar(__SIZE_TYPE__ n, int cond)
+{
+  void *p;
+  if (cond)
+    p = foo (n);
+  else
+    p = __builtin_malloc (n);
+
+  return p;
+}
+
+/* { dg-final { scan-ipa-dump "Function foo found to be malloc" "pure-const" } } */
+/* { dg-final { scan-ipa-dump "Function bar found to be malloc" "pure-const" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/propmalloc-3.c b/gcc/testsuite/gcc.dg/ipa/propmalloc-3.c
new file mode 100644 (file)
index 0000000..13558dd
--- /dev/null
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-pure-const-details" } */
+
+static void *foo(__SIZE_TYPE__, int) __attribute__((noinline, no_icf, used));
+
+__attribute__((noinline, used, no_icf))
+static void *bar(__SIZE_TYPE__ n, int m)
+{
+  return foo (n, m);
+}
+
+static void *foo(__SIZE_TYPE__ n, int m)
+{
+  void *p;
+  if (m > 0)
+    p = bar (n, --m);
+  else
+    p = __builtin_malloc (n);
+
+  return p;
+}
+
+/* { dg-final { scan-ipa-dump "Function foo found to be malloc" "pure-const" } } */
+/* { dg-final { scan-ipa-dump "Function bar found to be malloc" "pure-const" } } */