On 11/12/2014 02:10 PM, Jakub Jelinek wrote:
On Wed, Nov 12, 2014 at 12:46:48PM +0400, Maxim Ostapenko wrote:
If in the future we e.g. IPA-prop propagate the nonfreeing_call_p
property through the callgraph (as in, if the function you call
is non-overridable and you know the flag for it, use it),
FYI we tried this on SPEC and some other apps but saw no performance
improvements.

Yes, we have a patch for this kind of analysis, but it doesn't produce
reasonable performance improvements indeed. Anyway, we are able to perform
measurements once again on top of your patch if you decide to commit it.
If you have a patch written for the IPA propagation of nonfreeing_call_p,
can you post it?  Even if you don't see significant improvements from it,
if the pass isn't too costly (especially if it can be propagated in
some existing pass together with other analysis), then it might sense to add
it anyway.  nonfreeing_call_p isn't used just by asan.

        Jakub


We used this code for IPA propagation of nonfreeing_call_p. It implemented with a separate pass, but it probably could be propagated in some existing one. This analysis doesn't seem to be costly thought, we didn't see any significant slowdown compiling big files.

-Maxim
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 0ab3476..cae9f21 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1380,6 +1380,7 @@ OBJS = \
 	trans-mem.o \
 	tree-affine.o \
 	asan.o \
+	ipa-nonfree.o \
 	tsan.o \
 	ubsan.o \
 	sanopt.o \
@@ -2322,6 +2323,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
   $(srcdir)/ipa-inline.h \
   $(srcdir)/vtable-verify.c \
   $(srcdir)/asan.c \
+  $(srcdir)/ipa-nonfree.c \
   $(srcdir)/ubsan.c \
   $(srcdir)/tsan.c \
   $(srcdir)/sanopt.c \
diff --git a/gcc/gimple.c b/gcc/gimple.c
index 62f172b..b87dd71 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -2539,7 +2539,8 @@ nonfreeing_call_p (gimple call)
 	  return true;
       }
 
-  return false;
+  tree decl = gimple_call_fndecl (call);
+  return decl && lookup_attribute ("nonfreeing", DECL_ATTRIBUTES (decl));
 }
 
 /* Callback for walk_stmt_load_store_ops.
diff --git a/gcc/ipa-nonfree.c b/gcc/ipa-nonfree.c
new file mode 100644
index 0000000..1820e65
--- /dev/null
+++ b/gcc/ipa-nonfree.c
@@ -0,0 +1,206 @@
+/* Nonfreeing functions discovering decision heuristics.
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* For each defined function in compiling file, nonfree pass tries to
+   understand if this function is nonfreeing (does not free any allocated
+   memory).  Each such function is marked with "nonfreeing" attribute, which
+   existence will be checked in nonfreeing_call_p function later.  All external
+   routines and local, calling some external ones, are assumed to not be
+   nofreeing.  Functions having indirect calls are also nofreeing.  Function
+   is nonfreeing if and only if it calls only nonfreeing functions.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "tm.h"
+#include "hard-reg-set.h"
+#include "function.h"
+#include "is-a.h"
+#include "hash-map.h"
+#include "plugin-api.h"
+#include "ipa-ref.h"
+#include "cgraph.h"
+#include "stringpool.h"
+#include "tree-pass.h"
+
+/* This enum describes three possible statuses of functions in terms of its to
+   be nonfreeing.  Value STATUS_UNKNOWN means that it is not determined for
+   given function whether it is nonfreeing or not.  Value STATUS_NONFREEING
+   means that it is nonfreeing and STATUS_FREEING - that it is definitely
+   freeing.  */
+
+enum freeing_status {
+  STATUS_UNKNOWN,
+  STATUS_NONFREEING,
+  STATUS_FREEING
+};
+
+/* This vector contains cached information about each node in cgraph related to
+   freeing property.  */
+
+static vec<enum freeing_status> freeing_status_cached;
+
+/* Returns true if function can be interposed by linker (static or dynamic). */
+
+static inline bool
+is_interposable (struct symtab_node *node)
+{
+  return node->get_availability () <= AVAIL_INTERPOSABLE
+	 || node->can_be_discarded_p ();
+}
+
+/* Returns current freeing status of function NODE.  */
+
+static enum freeing_status
+get_freeing_status (struct cgraph_node *node)
+{
+  static bool inited;
+
+  static const char *freeing_fn_names[] = {
+    "free",
+    "realloc",
+    "_ITM_free",
+    "__builtin_free",
+    "__builtin_realloc",
+    "__builtin_stack_restore",
+    "operator delete",
+    "operator delete[]"
+  };
+
+  static tree freeing_fn_idents[ARRAY_SIZE (freeing_fn_names)];
+  if (!inited)
+    {
+      for (size_t i = 0; i < ARRAY_SIZE (freeing_fn_idents); ++i)
+	freeing_fn_idents[i] = get_identifier (freeing_fn_names[i]);
+
+      inited = true;
+    }
+
+  if (freeing_status_cached[node->uid] != STATUS_UNKNOWN)
+    return freeing_status_cached[node->uid];
+
+  if ((!node->has_gimple_body_p () && !node->alias)
+      || node->cpp_implicit_alias
+      || node->indirect_calls
+      || is_interposable (node))
+    return freeing_status_cached[node->uid] = STATUS_FREEING;
+
+  const tree decl = node->decl;
+  for (size_t i = 0; i < ARRAY_SIZE (freeing_fn_idents); ++i)
+    if (DECL_NAME (decl) == freeing_fn_idents[i])
+      return freeing_status_cached[node->uid] = STATUS_FREEING;
+
+  if (DECL_IS_BUILTIN (decl))
+    return freeing_status_cached[node->uid] = STATUS_NONFREEING;
+
+  return STATUS_UNKNOWN;
+}
+
+/* This function marks defined functions as
+   nonfreeing if this can be proven.  */
+
+static int
+find_nonfreeing (void)
+{
+  if (!symtab->cgraph_max_uid)
+    return 0;
+
+  freeing_status_cached.safe_grow_cleared (symtab->cgraph_max_uid);
+
+  bool changed;
+
+  do
+    {
+      changed = false;
+      struct cgraph_node *node;
+
+      FOR_EACH_DEFINED_FUNCTION (node)
+	{
+	  if (get_freeing_status (node) != STATUS_UNKNOWN)
+	    continue;
+
+	  struct cgraph_edge *edge;
+	  freeing_status status = STATUS_NONFREEING;
+	  for (edge = node->ultimate_alias_target ()->callees;
+	       edge && (node == edge->callee || status == STATUS_NONFREEING);
+	       edge = edge->next_callee)
+	    if (node != edge->callee)
+	      status = get_freeing_status (edge->callee);
+
+	  if (status != STATUS_UNKNOWN)
+	    {
+	      if (status == STATUS_NONFREEING)
+		DECL_ATTRIBUTES (node->decl)
+		  = tree_cons (get_identifier ("nonfreeing"), NULL,
+			       DECL_ATTRIBUTES (node->decl));
+	      freeing_status_cached[node->uid] = status;
+	      changed = true;
+	    }
+	 }
+    }
+  while (changed);
+
+  if (dump_file)
+    {
+      struct cgraph_node *node;
+      FOR_EACH_DEFINED_FUNCTION (node)
+	fprintf (dump_file, "Function %s is %sfreeing\n", fndecl_name (node->decl),
+	         freeing_status_cached[node->uid] == STATUS_NONFREEING ? "non" : "");
+    }
+
+  freeing_status_cached.release ();
+
+  return 0;
+}
+
+namespace {
+
+const pass_data pass_data_nonfree =
+{
+  SIMPLE_IPA_PASS, /* type */
+  "nonfree", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_nonfree : public simple_ipa_opt_pass
+{
+public:
+  pass_nonfree (gcc::context *ctxt)
+    : simple_ipa_opt_pass (pass_data_nonfree, ctxt)
+  {}
+
+  /* nonfree_pass methods: */
+  virtual unsigned int execute (function *) { return find_nonfreeing (); }
+
+}; // class pass_nonfree
+
+} // anon namespace
+
+simple_ipa_opt_pass *
+make_pass_nonfree (gcc::context *ctxt)
+{
+    return new pass_nonfree (ctxt);
+}
diff --git a/gcc/passes.def b/gcc/passes.def
index 2305d67..de9f7cd 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -136,6 +136,7 @@ along with GCC; see the file COPYING3.  If not see
      passes are executed after partitioning and thus see just parts of the
      compiled unit.  */
   INSERT_PASSES_AFTER (all_late_ipa_passes)
+  NEXT_PASS (pass_nonfree);
   NEXT_PASS (pass_ipa_pta);
   NEXT_PASS (pass_omp_simd_clone);
   TERMINATE_PASS_LIST ()
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index a3efdd8..ffcc5f9 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -459,6 +459,7 @@ extern simple_ipa_opt_pass *make_pass_build_ssa_passes (gcc::context *ctxt);
 extern simple_ipa_opt_pass *make_pass_chkp_instrumentation_passes (gcc::context *ctxt);
 extern simple_ipa_opt_pass *make_pass_local_optimization_passes (gcc::context *ctxt);
 
+extern simple_ipa_opt_pass *make_pass_nonfree (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_whole_program_visibility (gcc::context
 							       *ctxt);
 extern simple_ipa_opt_pass *make_pass_ipa_increase_alignment (gcc::context

Reply via email to