diff mbox

Optimize UBSAN_NULL checks, add sanopt.c

Message ID 54633B3A.2020304@partner.samsung.com
State New
Headers show

Commit Message

max Nov. 12, 2014, 10:49 a.m. UTC
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 mbox

Patch

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