public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] Optimize UBSAN_NULL checks, add sanopt.c
@ 2014-11-03 14:28 Marek Polacek
  2014-11-03 15:35 ` Jakub Jelinek
  2014-11-05  9:19 ` Yury Gribov
  0 siblings, 2 replies; 49+ messages in thread
From: Marek Polacek @ 2014-11-03 14:28 UTC (permalink / raw)
  To: GCC Patches, Jakub Jelinek

Another shot at optimizing redundant UBSAN_NULL statements.

This time we walk the dominator tree - that should result in
more effective optimization - and keep a list of UBSAN_NULL
statements that dominate the current block, see the comment
before sanopt_optimize_walker.  Statements coming from blocks
that are left during the CFG walk are lazily removed, but I
think that isn't really necessary: see the ??? comment.

E.g. on http://ur1.ca/iohtf this allowed us to drop 8 stmts.

I moved all of this into new sanopt.c file.
(I guess that file includes some headers that we in fact don't
need, but the header flattening doesn't make it easy to check,
there are too many of them.)

Bootstrapped(-ubsan)/regtested on x86_64-linux, ok for trunk?

2014-11-03  Marek Polacek  <polacek@redhat.com>

	* Makefile.in (OBJS): Add sanopt.o.
	(GTFILES): Add sanopt.c.
	* asan.h (asan_expand_check_ifn): Declare.
	* asan.c (asan_expand_check_ifn): No longer static.
	(class pass_sanopt, pass_sanopt::execute, make_pass_sanopt): Move...
	* sanopt.c: ...here.  New file.
testsuite/
	* c-c++-common/ubsan/align-2.c: Remove dg-output.
	* c-c++-common/ubsan/align-4.c: Likewise.
	* g++.dg/ubsan/null-1.C: Likewise.
	* g++.dg/ubsan/null-2.C: Likewise.

diff --git gcc/Makefile.in gcc/Makefile.in
index 9c67fe2..f383032 100644
--- gcc/Makefile.in
+++ gcc/Makefile.in
@@ -1376,6 +1376,7 @@ OBJS = \
 	asan.o \
 	tsan.o \
 	ubsan.o \
+	sanopt.o \
 	tree-call-cdce.o \
 	tree-cfg.o \
 	tree-cfgcleanup.o \
@@ -2305,6 +2306,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
   $(srcdir)/asan.c \
   $(srcdir)/ubsan.c \
   $(srcdir)/tsan.c \
+  $(srcdir)/sanopt.c \
   $(srcdir)/ipa-devirt.c \
   $(srcdir)/internal-fn.h \
   @all_gtfiles@
diff --git gcc/asan.c gcc/asan.c
index 8f146d2..79dede7 100644
--- gcc/asan.c
+++ gcc/asan.c
@@ -2497,7 +2497,7 @@ asan_finish_file (void)
 
 /* Expand the ASAN_{LOAD,STORE} builtins.  */
 
-static bool
+bool
 asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
 {
   gimple g = gsi_stmt (*iter);
@@ -2800,114 +2800,4 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
-/* Perform optimization of sanitize functions.  */
-
-namespace {
-
-const pass_data pass_data_sanopt =
-{
-  GIMPLE_PASS, /* type */
-  "sanopt", /* name */
-  OPTGROUP_NONE, /* optinfo_flags */
-  TV_NONE, /* tv_id */
-  ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
-  0, /* properties_provided */
-  0, /* properties_destroyed */
-  0, /* todo_flags_start */
-  TODO_update_ssa, /* todo_flags_finish */
-};
-
-class pass_sanopt : public gimple_opt_pass
-{
-public:
-  pass_sanopt (gcc::context *ctxt)
-    : gimple_opt_pass (pass_data_sanopt, ctxt)
-  {}
-
-  /* opt_pass methods: */
-  virtual bool gate (function *) { return flag_sanitize; }
-  virtual unsigned int execute (function *);
-
-}; // class pass_sanopt
-
-unsigned int
-pass_sanopt::execute (function *fun)
-{
-  basic_block bb;
-
-  int asan_num_accesses = 0;
-  if (flag_sanitize & SANITIZE_ADDRESS)
-    {
-      gimple_stmt_iterator gsi;
-      FOR_EACH_BB_FN (bb, fun)
-	for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
-	  {
- 	    gimple stmt = gsi_stmt (gsi);
-	    if (is_gimple_call (stmt) && gimple_call_internal_p (stmt)
-		&& gimple_call_internal_fn (stmt) == IFN_ASAN_CHECK)
-	      ++asan_num_accesses;
-	  }
-    }
-
-  bool use_calls = ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD < INT_MAX
-    && asan_num_accesses >= ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD;
-
-  FOR_EACH_BB_FN (bb, fun)
-    {
-      gimple_stmt_iterator gsi;
-      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
-	{
-	  gimple stmt = gsi_stmt (gsi);
-	  bool no_next = false;
-
-	  if (!is_gimple_call (stmt))
-	    {
-	      gsi_next (&gsi);
-	      continue;
-	    }
-
-	  if (gimple_call_internal_p (stmt))
-	    {
-	      enum internal_fn ifn = gimple_call_internal_fn (stmt);
-	      switch (ifn)
-		{
-		case IFN_UBSAN_NULL:
-		  no_next = ubsan_expand_null_ifn (&gsi);
-		  break;
-		case IFN_UBSAN_BOUNDS:
-		  no_next = ubsan_expand_bounds_ifn (&gsi);
-		  break;
-		case IFN_UBSAN_OBJECT_SIZE:
-		  no_next = ubsan_expand_objsize_ifn (&gsi);
-		  break;
-		case IFN_ASAN_CHECK:
-		  no_next = asan_expand_check_ifn (&gsi, use_calls);
-		  break;
-		default:
-		  break;
-		}
-	    }
-
-	  if (dump_file && (dump_flags & TDF_DETAILS))
-	    {
-	      fprintf (dump_file, "Optimized\n  ");
-	      print_gimple_stmt (dump_file, stmt, 0, dump_flags);
-	      fprintf (dump_file, "\n");
-	    }
-
-	  if (!no_next)
-	    gsi_next (&gsi);
-	}
-    }
-  return 0;
-}
-
-} // anon namespace
-
-gimple_opt_pass *
-make_pass_sanopt (gcc::context *ctxt)
-{
-  return new pass_sanopt (ctxt);
-}
-
 #include "gt-asan.h"
diff --git gcc/asan.h gcc/asan.h
index 8e3f0ba..f448391 100644
--- gcc/asan.h
+++ gcc/asan.h
@@ -28,6 +28,7 @@ extern rtx_insn *asan_emit_stack_protection (rtx, rtx, unsigned int,
 extern bool asan_protect_global (tree);
 extern void initialize_sanitizer_builtins (void);
 extern tree asan_dynamic_init_call (bool);
+extern bool asan_expand_check_ifn (gimple_stmt_iterator *, bool);
 
 extern gimple_stmt_iterator create_cond_insert_point
      (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
diff --git gcc/sanopt.c gcc/sanopt.c
index e69de29..b8d6183 100644
--- gcc/sanopt.c
+++ gcc/sanopt.c
@@ -0,0 +1,316 @@
+/* Optimize and expand sanitizer functions.
+   Copyright (C) 2014 Free Software Foundation, Inc.
+   Contributed by Marek Polacek <polacek@redhat.com>
+
+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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "hash-table.h"
+#include "predict.h"
+#include "vec.h"
+#include "hashtab.h"
+#include "hash-set.h"
+#include "machmode.h"
+#include "tm.h"
+#include "hard-reg-set.h"
+#include "input.h"
+#include "function.h"
+#include "dominance.h"
+#include "cfg.h"
+#include "cfganal.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "inchash.h"
+#include "gimple.h"
+#include "gimplify.h"
+#include "gimple-iterator.h"
+#include "calls.h"
+#include "varasm.h"
+#include "stor-layout.h"
+#include "hash-map.h"
+#include "plugin-api.h"
+#include "ipa-ref.h"
+#include "cgraph.h"
+#include "stringpool.h"
+#include "tree-ssanames.h"
+#include "tree-pass.h"
+#include "asan.h"
+#include "gimple-pretty-print.h"
+#include "target.h"
+#include "expr.h"
+#include "output.h"
+#include "tm_p.h"
+#include "langhooks.h"
+#include "ubsan.h"
+#include "params.h"
+
+
+/* This is used to carry information about basic blocks.  It is
+   attached to the AUX field of the standard CFG block.  */
+
+struct sanopt_info
+{
+  /* True if this BB has been visited.  */
+  bool visited_p;
+};
+
+
+/* Try to optimize away redundant UBSAN_NULL checks.
+   
+   We walk blocks in the CFG via a depth first search of the dominator
+   tree; we push unique UBSAN_NULL statements into a vector in the
+   NULL_CHECK_MAP as we enter the blocks.  When leaving a block, we
+   mark the block as visited; then when checking the statements in the
+   vector, we ignore statements that are coming from already visited
+   blocks, because these cannot dominate anything anymore.  */
+
+static void
+sanopt_optimize_walker (basic_block bb,
+			hash_map<tree, auto_vec<gimple> > *map)
+{
+  basic_block son;
+  gimple_stmt_iterator gsi;
+
+  for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
+    {
+      gimple stmt = gsi_stmt (gsi);
+      bool remove = false;
+
+      if (is_gimple_call (stmt)
+	  && gimple_call_internal_p (stmt))
+	switch (gimple_call_internal_fn (stmt))
+	  {
+	  case IFN_UBSAN_NULL:
+	    {
+	      gcc_assert (gimple_call_num_args (stmt) == 3);
+	      tree ptr = gimple_call_arg (stmt, 0);
+	      tree cur_align = gimple_call_arg (stmt, 2);
+	      gcc_assert (TREE_CODE (cur_align) == INTEGER_CST);
+
+	      auto_vec<gimple> &v = map->get_or_insert (ptr);
+	      if (v.is_empty ())
+		/* For this PTR we don't have any UBSAN_NULL stmts
+		   recorded, so there's nothing to optimize yet.  */
+		v.safe_push (stmt);
+	      else
+		{
+		  /* We already have recorded a UBSAN_NULL check
+		     for this pointer.  Perhaps we can drop this one.
+		     But only if this check doesn't specify stricter
+		     alignment.  */
+		  int i;
+		  gimple g;
+
+		  FOR_EACH_VEC_ELT (v, i, g)
+		    {
+		      /* Remove statements for BBs that have been
+			 already processed.  */
+		      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
+		      if (si->visited_p)
+			{
+			  /* ??? This might be unneccesary; we could just
+			     skip the stale statements.  */
+			  v.unordered_remove (i);
+			  continue;
+			}
+		      tree align = gimple_call_arg (g, 2);
+		      if (tree_int_cst_le (cur_align, align))
+			{
+			  remove = true;
+			  break;
+			}
+		    }
+
+		  if (remove)
+		    {
+		      /* Drop this check.  */
+		      if (dump_file && (dump_flags & TDF_DETAILS))
+			{
+			  fprintf (dump_file, "Optimizing out\n  ");
+			  print_gimple_stmt (dump_file, stmt, 0,
+					     dump_flags);
+			  fprintf (dump_file, "\n");
+			}
+		      gsi_remove (&gsi, true);
+		    }
+		  else if (v.length () < 30)
+		    v.safe_push (stmt);
+		  }
+	    }
+	  default:
+	    break;
+	  }
+
+      /* If we were able to remove the current statement, gsi_remove
+	 already pointed us to the next statement.  */
+      if (!remove)
+	gsi_next (&gsi);
+    }
+
+  for (son = first_dom_son (CDI_DOMINATORS, bb);
+       son;
+       son = next_dom_son (CDI_DOMINATORS, son))
+    sanopt_optimize_walker (son, map);
+
+  /* We're leaving this BB, so mark it to that effect.  */
+  sanopt_info *info = (sanopt_info *) bb->aux;
+  info->visited_p = true;
+}
+
+/* Try to remove redundant sanitizer checks in function FUN.  */
+
+static void
+sanopt_optimize (function *fun)
+{
+  /* This map maps a pointer (the first argument of UBSAN_NULL) to
+     a vector of UBSAN_NULL call statements that check this pointer.  */
+  hash_map<tree, auto_vec<gimple> > null_check_map;
+
+  /* Set up block info for each basic block.  */
+  alloc_aux_for_blocks (sizeof (sanopt_info));
+
+  /* We're going to do a dominator walk, so ensure that we have
+     dominance information.  */
+  calculate_dominance_info (CDI_DOMINATORS);
+
+  /* Recursively walk the dominator tree optimizing away
+     redundant checks.  */
+  sanopt_optimize_walker (ENTRY_BLOCK_PTR_FOR_FN (fun), &null_check_map);
+
+  free_aux_for_blocks ();
+}
+
+/* Perform optimization of sanitize functions.  */
+
+namespace {
+
+const pass_data pass_data_sanopt =
+{
+  GIMPLE_PASS, /* type */
+  "sanopt", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_update_ssa, /* todo_flags_finish */
+};
+
+class pass_sanopt : public gimple_opt_pass
+{
+public:
+  pass_sanopt (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_sanopt, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *) { return flag_sanitize; }
+  virtual unsigned int execute (function *);
+
+}; // class pass_sanopt
+
+unsigned int
+pass_sanopt::execute (function *fun)
+{
+  basic_block bb;
+
+  /* Try to remove redundant checks.  */
+  if (optimize
+      && (flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT)))
+    sanopt_optimize (fun);
+
+  int asan_num_accesses = 0;
+  if (flag_sanitize & SANITIZE_ADDRESS)
+    {
+      gimple_stmt_iterator gsi;
+      FOR_EACH_BB_FN (bb, fun)
+	for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+	  {
+ 	    gimple stmt = gsi_stmt (gsi);
+	    if (is_gimple_call (stmt) && gimple_call_internal_p (stmt)
+		&& gimple_call_internal_fn (stmt) == IFN_ASAN_CHECK)
+	      ++asan_num_accesses;
+	  }
+    }
+
+  bool use_calls = ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD < INT_MAX
+    && asan_num_accesses >= ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD;
+
+  FOR_EACH_BB_FN (bb, fun)
+    {
+      gimple_stmt_iterator gsi;
+      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
+	{
+	  gimple stmt = gsi_stmt (gsi);
+	  bool no_next = false;
+
+	  if (!is_gimple_call (stmt))
+	    {
+	      gsi_next (&gsi);
+	      continue;
+	    }
+
+	  if (gimple_call_internal_p (stmt))
+	    {
+	      enum internal_fn ifn = gimple_call_internal_fn (stmt);
+	      switch (ifn)
+		{
+		case IFN_UBSAN_NULL:
+		  no_next = ubsan_expand_null_ifn (&gsi);
+		  break;
+		case IFN_UBSAN_BOUNDS:
+		  no_next = ubsan_expand_bounds_ifn (&gsi);
+		  break;
+		case IFN_UBSAN_OBJECT_SIZE:
+		  no_next = ubsan_expand_objsize_ifn (&gsi);
+		  break;
+		case IFN_ASAN_CHECK:
+		  no_next = asan_expand_check_ifn (&gsi, use_calls);
+		  break;
+		default:
+		  break;
+		}
+	    }
+
+	  if (dump_file && (dump_flags & TDF_DETAILS))
+	    {
+	      fprintf (dump_file, "Expanded\n  ");
+	      print_gimple_stmt (dump_file, stmt, 0, dump_flags);
+	      fprintf (dump_file, "\n");
+	    }
+
+	  if (!no_next)
+	    gsi_next (&gsi);
+	}
+    }
+  return 0;
+}
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_sanopt (gcc::context *ctxt)
+{
+  return new pass_sanopt (ctxt);
+}
diff --git gcc/testsuite/c-c++-common/ubsan/align-2.c gcc/testsuite/c-c++-common/ubsan/align-2.c
index 071de8c..02a26e2 100644
--- gcc/testsuite/c-c++-common/ubsan/align-2.c
+++ gcc/testsuite/c-c++-common/ubsan/align-2.c
@@ -51,6 +51,4 @@ main ()
 /* { dg-output "\.c:(13|16):\[0-9]*: \[^\n\r]*store to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" } */
 /* { dg-output "\.c:23:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
 /* { dg-output "\.c:(29|30):\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
-/* { dg-output "\.c:30:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
-/* { dg-output "\.c:31:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
 /* { dg-output "\.c:37:\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'long long int', which requires \[48] byte alignment" } */
diff --git gcc/testsuite/c-c++-common/ubsan/align-4.c gcc/testsuite/c-c++-common/ubsan/align-4.c
index 3252595..f010589 100644
--- gcc/testsuite/c-c++-common/ubsan/align-4.c
+++ gcc/testsuite/c-c++-common/ubsan/align-4.c
@@ -9,6 +9,4 @@
 /* { dg-output "\[^\n\r]*\.c:(13|16):\[0-9]*: \[^\n\r]*store to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" } */
 /* { dg-output "\[^\n\r]*\.c:23:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
 /* { dg-output "\[^\n\r]*\.c:(29|30):\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
-/* { dg-output "\[^\n\r]*\.c:30:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
-/* { dg-output "\[^\n\r]*\.c:31:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
 /* { dg-output "\[^\n\r]*\.c:37:\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'long long int', which requires \[48] byte alignment" } */
diff --git gcc/testsuite/g++.dg/ubsan/null-1.C gcc/testsuite/g++.dg/ubsan/null-1.C
index e1524b1..83b3033 100644
--- gcc/testsuite/g++.dg/ubsan/null-1.C
+++ gcc/testsuite/g++.dg/ubsan/null-1.C
@@ -25,6 +25,4 @@ main (void)
 }
 
 // { dg-output "reference binding to null pointer of type 'int'(\n|\r\n|\r)" }
-// { dg-output "\[^\n\r]*reference binding to null pointer of type 'int'(\n|\r\n|\r)" }
 // { dg-output "\[^\n\r]*reference binding to null pointer of type 'const L'(\n|\r\n|\r)" }
-// { dg-output "\[^\n\r]*reference binding to null pointer of type 'int'(\n|\r\n|\r)" }
diff --git gcc/testsuite/g++.dg/ubsan/null-2.C gcc/testsuite/g++.dg/ubsan/null-2.C
index 88f387e..0230c7c 100644
--- gcc/testsuite/g++.dg/ubsan/null-2.C
+++ gcc/testsuite/g++.dg/ubsan/null-2.C
@@ -35,5 +35,3 @@ main (void)
 
 // { dg-output "\.C:26:\[0-9]*:\[\^\n\r]*member call on null pointer of type 'struct U'.*" }
 // { dg-output "\.C:29:\[0-9]*:\[\^\n\r]*member call on null pointer of type 'struct V'.*" }
-// { dg-output "\.C:31:\[0-9]*:\[\^\n\r]*member call on null pointer of type 'struct V'.*" }
-// { dg-output "\.C:33:\[0-9]*:\[\^\n\r]*member call on null pointer of type 'struct V'" }

	Marek

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize UBSAN_NULL checks, add sanopt.c
  2014-11-03 14:28 [PATCH] Optimize UBSAN_NULL checks, add sanopt.c Marek Polacek
@ 2014-11-03 15:35 ` Jakub Jelinek
  2014-11-04 18:36   ` Marek Polacek
  2014-11-05  9:19 ` Yury Gribov
  1 sibling, 1 reply; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-03 15:35 UTC (permalink / raw)
  To: Marek Polacek; +Cc: GCC Patches

On Mon, Nov 03, 2014 at 03:27:57PM +0100, Marek Polacek wrote:
> I moved all of this into new sanopt.c file.
> (I guess that file includes some headers that we in fact don't
> need, but the header flattening doesn't make it easy to check,
> there are too many of them.)

Well, in theory you could just script removing them one by one and just
make sanopt.o after each step to see if the header is or is not needed,
perhaps with some manual tweeks.

> +/* This is used to carry information about basic blocks.  It is
> +   attached to the AUX field of the standard CFG block.  */
> +
> +struct sanopt_info
> +{
> +  /* True if this BB has been visited.  */
> +  bool visited_p;
> +};
> +
> +
> +/* Try to optimize away redundant UBSAN_NULL checks.
> +   
> +   We walk blocks in the CFG via a depth first search of the dominator
> +   tree; we push unique UBSAN_NULL statements into a vector in the
> +   NULL_CHECK_MAP as we enter the blocks.  When leaving a block, we
> +   mark the block as visited; then when checking the statements in the
> +   vector, we ignore statements that are coming from already visited
> +   blocks, because these cannot dominate anything anymore.  */
> +
> +static void
> +sanopt_optimize_walker (basic_block bb,
> +			hash_map<tree, auto_vec<gimple> > *map)

Perhaps in preparation for future optimizations (other UBSAN_*
calls, and ASAN_CHECK and tsan builtins), you should consider
putting the hash_map into some structure and pass address of that
structure instead, so that you have all the pass context at the same spot.
You could put asan_num_accesses in there too, see below.

> +		  /* We already have recorded a UBSAN_NULL check
> +		     for this pointer.  Perhaps we can drop this one.
> +		     But only if this check doesn't specify stricter
> +		     alignment.  */
> +		  int i;
> +		  gimple g;
> +
> +		  FOR_EACH_VEC_ELT (v, i, g)
> +		    {
> +		      /* Remove statements for BBs that have been
> +			 already processed.  */
> +		      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
> +		      if (si->visited_p)
> +			{
> +			  /* ??? This might be unneccesary; we could just
> +			     skip the stale statements.  */
> +			  v.unordered_remove (i);
> +			  continue;
> +			}

I think it would be better to walk the vector backwards instead of forward.
1) if you have the same SSA_NAME there multiple times, ignoring the already
   unnecessary stmts, the only case where you'd have multiple stmts is
   if the earlier stmts dominate the following stmts and for some reason
   weren't optimized away; that for some reason currently should be
   just higher required alignment in the dominated stmt (e.g. say have
   UBSAN_NULL (foo_23, 0); UBSAN_NULL (foo_23, 2); UBSAN_NULL (foo_23, 4);
   where the first stmt dominates the second two and second stmt dominates
   the last one.
2) All the si->visited_p stmts should be always at the end of the vector
   IMHO, preceeded only by !si->visited_p stmts.
Thus, when walking backwards, first remove the stmts with bb->visited_p,
once you hit one that doesn't have it set, I believe there shouldn't be any
further.  And then in theory it should be fine to just compare the last
stmt in the vector that was left (if any).

> +unsigned int
> +pass_sanopt::execute (function *fun)
> +{
> +  basic_block bb;
> +
> +  /* Try to remove redundant checks.  */
> +  if (optimize
> +      && (flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT)))
> +    sanopt_optimize (fun);

Perhaps you could return the asan_num_accesses value computed during
sanopt_optimize (just count IFN_ASAN_CHECK calls that you don't optimize
away), and do this only in else if (i.e. when sanopt_optimize has not been
run).  That way, you'd save one extra IL walk.

> +  if (flag_sanitize & SANITIZE_ADDRESS)
> +    {
> +      gimple_stmt_iterator gsi;
> +      FOR_EACH_BB_FN (bb, fun)
> +	for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
> +	  {
> + 	    gimple stmt = gsi_stmt (gsi);
> +	    if (is_gimple_call (stmt) && gimple_call_internal_p (stmt)
> +		&& gimple_call_internal_fn (stmt) == IFN_ASAN_CHECK)
> +	      ++asan_num_accesses;
> +	  }
> +    }

Otherwise LGTM, thanks for working on this.

	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize UBSAN_NULL checks, add sanopt.c
  2014-11-03 15:35 ` Jakub Jelinek
@ 2014-11-04 18:36   ` Marek Polacek
  2014-11-04 19:18     ` Jakub Jelinek
  0 siblings, 1 reply; 49+ messages in thread
From: Marek Polacek @ 2014-11-04 18:36 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

On Mon, Nov 03, 2014 at 04:35:00PM +0100, Jakub Jelinek wrote:
> Well, in theory you could just script removing them one by one and just
> make sanopt.o after each step to see if the header is or is not needed,
> perhaps with some manual tweeks.

I pruned those headers some more.
 
> Perhaps in preparation for future optimizations (other UBSAN_*
> calls, and ASAN_CHECK and tsan builtins), you should consider
> putting the hash_map into some structure and pass address of that
> structure instead, so that you have all the pass context at the same spot.
> You could put asan_num_accesses in there too, see below.
 
Done.

> I think it would be better to walk the vector backwards instead of forward.
> 1) if you have the same SSA_NAME there multiple times, ignoring the already
>    unnecessary stmts, the only case where you'd have multiple stmts is
>    if the earlier stmts dominate the following stmts and for some reason
>    weren't optimized away; that for some reason currently should be
>    just higher required alignment in the dominated stmt (e.g. say have
>    UBSAN_NULL (foo_23, 0); UBSAN_NULL (foo_23, 2); UBSAN_NULL (foo_23, 4);
>    where the first stmt dominates the second two and second stmt dominates
>    the last one.
> 2) All the si->visited_p stmts should be always at the end of the vector
>    IMHO, preceeded only by !si->visited_p stmts.
> Thus, when walking backwards, first remove the stmts with bb->visited_p,
> once you hit one that doesn't have it set, I believe there shouldn't be any
> further.  And then in theory it should be fine to just compare the last
> stmt in the vector that was left (if any).
 
I agree that should work - so changed.

> Perhaps you could return the asan_num_accesses value computed during
> sanopt_optimize (just count IFN_ASAN_CHECK calls that you don't optimize
> away), and do this only in else if (i.e. when sanopt_optimize has not been
> run).  That way, you'd save one extra IL walk.

Done.

Bootstrap-ubsan/regtest passed on x86_64-linux, ok for trunk?

2014-11-04  Marek Polacek  <polacek@redhat.com>

	* Makefile.in (OBJS): Add sanopt.o.
	(GTFILES): Add sanopt.c.
	* asan.h (asan_expand_check_ifn): Declare.
	* asan.c (asan_expand_check_ifn): No longer static.
	(class pass_sanopt, pass_sanopt::execute, make_pass_sanopt): Move...
	* sanopt.c: ...here.  New file.
testsuite/
	* c-c++-common/ubsan/align-2.c: Remove dg-output.
	* c-c++-common/ubsan/align-4.c: Likewise.
	* g++.dg/ubsan/null-1.C: Likewise.
	* g++.dg/ubsan/null-2.C: Likewise.

diff --git gcc/Makefile.in gcc/Makefile.in
index 9c67fe2..f383032 100644
--- gcc/Makefile.in
+++ gcc/Makefile.in
@@ -1376,6 +1376,7 @@ OBJS = \
 	asan.o \
 	tsan.o \
 	ubsan.o \
+	sanopt.o \
 	tree-call-cdce.o \
 	tree-cfg.o \
 	tree-cfgcleanup.o \
@@ -2305,6 +2306,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
   $(srcdir)/asan.c \
   $(srcdir)/ubsan.c \
   $(srcdir)/tsan.c \
+  $(srcdir)/sanopt.c \
   $(srcdir)/ipa-devirt.c \
   $(srcdir)/internal-fn.h \
   @all_gtfiles@
diff --git gcc/asan.c gcc/asan.c
index 8f146d2..79dede7 100644
--- gcc/asan.c
+++ gcc/asan.c
@@ -2497,7 +2497,7 @@ asan_finish_file (void)
 
 /* Expand the ASAN_{LOAD,STORE} builtins.  */
 
-static bool
+bool
 asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
 {
   gimple g = gsi_stmt (*iter);
@@ -2800,114 +2800,4 @@ make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
-/* Perform optimization of sanitize functions.  */
-
-namespace {
-
-const pass_data pass_data_sanopt =
-{
-  GIMPLE_PASS, /* type */
-  "sanopt", /* name */
-  OPTGROUP_NONE, /* optinfo_flags */
-  TV_NONE, /* tv_id */
-  ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
-  0, /* properties_provided */
-  0, /* properties_destroyed */
-  0, /* todo_flags_start */
-  TODO_update_ssa, /* todo_flags_finish */
-};
-
-class pass_sanopt : public gimple_opt_pass
-{
-public:
-  pass_sanopt (gcc::context *ctxt)
-    : gimple_opt_pass (pass_data_sanopt, ctxt)
-  {}
-
-  /* opt_pass methods: */
-  virtual bool gate (function *) { return flag_sanitize; }
-  virtual unsigned int execute (function *);
-
-}; // class pass_sanopt
-
-unsigned int
-pass_sanopt::execute (function *fun)
-{
-  basic_block bb;
-
-  int asan_num_accesses = 0;
-  if (flag_sanitize & SANITIZE_ADDRESS)
-    {
-      gimple_stmt_iterator gsi;
-      FOR_EACH_BB_FN (bb, fun)
-	for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
-	  {
- 	    gimple stmt = gsi_stmt (gsi);
-	    if (is_gimple_call (stmt) && gimple_call_internal_p (stmt)
-		&& gimple_call_internal_fn (stmt) == IFN_ASAN_CHECK)
-	      ++asan_num_accesses;
-	  }
-    }
-
-  bool use_calls = ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD < INT_MAX
-    && asan_num_accesses >= ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD;
-
-  FOR_EACH_BB_FN (bb, fun)
-    {
-      gimple_stmt_iterator gsi;
-      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
-	{
-	  gimple stmt = gsi_stmt (gsi);
-	  bool no_next = false;
-
-	  if (!is_gimple_call (stmt))
-	    {
-	      gsi_next (&gsi);
-	      continue;
-	    }
-
-	  if (gimple_call_internal_p (stmt))
-	    {
-	      enum internal_fn ifn = gimple_call_internal_fn (stmt);
-	      switch (ifn)
-		{
-		case IFN_UBSAN_NULL:
-		  no_next = ubsan_expand_null_ifn (&gsi);
-		  break;
-		case IFN_UBSAN_BOUNDS:
-		  no_next = ubsan_expand_bounds_ifn (&gsi);
-		  break;
-		case IFN_UBSAN_OBJECT_SIZE:
-		  no_next = ubsan_expand_objsize_ifn (&gsi);
-		  break;
-		case IFN_ASAN_CHECK:
-		  no_next = asan_expand_check_ifn (&gsi, use_calls);
-		  break;
-		default:
-		  break;
-		}
-	    }
-
-	  if (dump_file && (dump_flags & TDF_DETAILS))
-	    {
-	      fprintf (dump_file, "Optimized\n  ");
-	      print_gimple_stmt (dump_file, stmt, 0, dump_flags);
-	      fprintf (dump_file, "\n");
-	    }
-
-	  if (!no_next)
-	    gsi_next (&gsi);
-	}
-    }
-  return 0;
-}
-
-} // anon namespace
-
-gimple_opt_pass *
-make_pass_sanopt (gcc::context *ctxt)
-{
-  return new pass_sanopt (ctxt);
-}
-
 #include "gt-asan.h"
diff --git gcc/asan.h gcc/asan.h
index 8e3f0ba..f448391 100644
--- gcc/asan.h
+++ gcc/asan.h
@@ -28,6 +28,7 @@ extern rtx_insn *asan_emit_stack_protection (rtx, rtx, unsigned int,
 extern bool asan_protect_global (tree);
 extern void initialize_sanitizer_builtins (void);
 extern tree asan_dynamic_init_call (bool);
+extern bool asan_expand_check_ifn (gimple_stmt_iterator *, bool);
 
 extern gimple_stmt_iterator create_cond_insert_point
      (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
diff --git gcc/sanopt.c gcc/sanopt.c
index e69de29..7bfe59e 100644
--- gcc/sanopt.c
+++ gcc/sanopt.c
@@ -0,0 +1,315 @@
+/* Optimize and expand sanitizer functions.
+   Copyright (C) 2014 Free Software Foundation, Inc.
+   Contributed by Marek Polacek <polacek@redhat.com>
+
+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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "hash-table.h"
+#include "predict.h"
+#include "vec.h"
+#include "hashtab.h"
+#include "hash-set.h"
+#include "tm.h"
+#include "hard-reg-set.h"
+#include "function.h"
+#include "dominance.h"
+#include "cfg.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "gimplify.h"
+#include "gimple-iterator.h"
+#include "hash-map.h"
+#include "plugin-api.h"
+#include "tree-pass.h"
+#include "asan.h"
+#include "gimple-pretty-print.h"
+#include "tm_p.h"
+#include "langhooks.h"
+#include "ubsan.h"
+#include "params.h"
+
+
+/* This is used to carry information about basic blocks.  It is
+   attached to the AUX field of the standard CFG block.  */
+
+struct sanopt_info
+{
+  /* True if this BB has been visited.  */
+  bool visited_p;
+};
+
+/* This is used to carry various hash maps and variables used
+   in sanopt_optimize_walker.  */
+
+struct sanopt_ctx
+{
+  /* This map maps a pointer (the first argument of UBSAN_NULL) to
+     a vector of UBSAN_NULL call statements that check this pointer.  */
+  hash_map<tree, auto_vec<gimple> > null_check_map;
+
+  /* Number of IFN_ASAN_CHECK statements.  */
+  int asan_num_accesses;
+};
+
+
+/* Try to optimize away redundant UBSAN_NULL checks.
+   
+   We walk blocks in the CFG via a depth first search of the dominator
+   tree; we push unique UBSAN_NULL statements into a vector in the
+   NULL_CHECK_MAP as we enter the blocks.  When leaving a block, we
+   mark the block as visited; then when checking the statements in the
+   vector, we ignore statements that are coming from already visited
+   blocks, because these cannot dominate anything anymore.
+   CTX is a sanopt context.  */
+
+static void
+sanopt_optimize_walker (basic_block bb, struct sanopt_ctx *ctx)
+{
+  basic_block son;
+  gimple_stmt_iterator gsi;
+
+  for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
+    {
+      gimple stmt = gsi_stmt (gsi);
+      bool remove = false;
+
+      if (is_gimple_call (stmt)
+	  && gimple_call_internal_p (stmt))
+	switch (gimple_call_internal_fn (stmt))
+	  {
+	  case IFN_UBSAN_NULL:
+	    {
+	      gcc_assert (gimple_call_num_args (stmt) == 3);
+	      tree ptr = gimple_call_arg (stmt, 0);
+	      tree cur_align = gimple_call_arg (stmt, 2);
+	      gcc_assert (TREE_CODE (cur_align) == INTEGER_CST);
+
+	      auto_vec<gimple> &v = ctx->null_check_map.get_or_insert (ptr);
+	      if (v.is_empty ())
+		/* For this PTR we don't have any UBSAN_NULL stmts
+		   recorded, so there's nothing to optimize yet.  */
+		v.safe_push (stmt);
+	      else
+		{
+		  /* We already have recorded a UBSAN_NULL check
+		     for this pointer.  Perhaps we can drop this one.
+		     But only if this check doesn't specify stricter
+		     alignment.  */
+		  int i;
+		  gimple g;
+
+		  FOR_EACH_VEC_ELT_REVERSE (v, i, g)
+		    {
+		      /* Remove statements for BBs that have been
+			 already processed.  */
+		      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
+		      if (si->visited_p)
+			{
+			  v.unordered_remove (i);
+			  continue;
+			}
+		      /* At this point we shouldn't have any statements
+			 that aren't dominating the current BB.  */
+		      tree align = gimple_call_arg (g, 2);
+		      remove = tree_int_cst_le (cur_align, align);
+		      break;
+		    }
+
+		  if (remove)
+		    {
+		      /* Drop this check.  */
+		      if (dump_file && (dump_flags & TDF_DETAILS))
+			{
+			  fprintf (dump_file, "Optimizing out\n  ");
+			  print_gimple_stmt (dump_file, stmt, 0,
+					     dump_flags);
+			  fprintf (dump_file, "\n");
+			}
+		      gsi_remove (&gsi, true);
+		    }
+		  else if (v.length () < 30)
+		    v.safe_push (stmt);
+		  }
+	    }
+	  case IFN_ASAN_CHECK:
+	    ctx->asan_num_accesses++;
+	    break;
+	  default:
+	    break;
+	  }
+
+      /* If we were able to remove the current statement, gsi_remove
+	 already pointed us to the next statement.  */
+      if (!remove)
+	gsi_next (&gsi);
+    }
+
+  for (son = first_dom_son (CDI_DOMINATORS, bb);
+       son;
+       son = next_dom_son (CDI_DOMINATORS, son))
+    sanopt_optimize_walker (son, ctx);
+
+  /* We're leaving this BB, so mark it to that effect.  */
+  sanopt_info *info = (sanopt_info *) bb->aux;
+  info->visited_p = true;
+}
+
+/* Try to remove redundant sanitizer checks in function FUN.  */
+
+static int
+sanopt_optimize (function *fun)
+{
+  struct sanopt_ctx ctx;
+  ctx.asan_num_accesses = 0;
+
+  /* Set up block info for each basic block.  */
+  alloc_aux_for_blocks (sizeof (sanopt_info));
+
+  /* We're going to do a dominator walk, so ensure that we have
+     dominance information.  */
+  calculate_dominance_info (CDI_DOMINATORS);
+
+  /* Recursively walk the dominator tree optimizing away
+     redundant checks.  */
+  sanopt_optimize_walker (ENTRY_BLOCK_PTR_FOR_FN (fun), &ctx);
+
+  free_aux_for_blocks ();
+
+  return ctx.asan_num_accesses;
+}
+
+/* Perform optimization of sanitize functions.  */
+
+namespace {
+
+const pass_data pass_data_sanopt =
+{
+  GIMPLE_PASS, /* type */
+  "sanopt", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_update_ssa, /* todo_flags_finish */
+};
+
+class pass_sanopt : public gimple_opt_pass
+{
+public:
+  pass_sanopt (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_sanopt, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *) { return flag_sanitize; }
+  virtual unsigned int execute (function *);
+
+}; // class pass_sanopt
+
+unsigned int
+pass_sanopt::execute (function *fun)
+{
+  basic_block bb;
+  int asan_num_accesses = 0;
+
+  /* Try to remove redundant checks.  */
+  if (optimize
+      && (flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT)))
+    asan_num_accesses = sanopt_optimize (fun);
+  else if (flag_sanitize & SANITIZE_ADDRESS)
+    {
+      gimple_stmt_iterator gsi;
+      FOR_EACH_BB_FN (bb, fun)
+	for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+	  {
+ 	    gimple stmt = gsi_stmt (gsi);
+	    if (is_gimple_call (stmt) && gimple_call_internal_p (stmt)
+		&& gimple_call_internal_fn (stmt) == IFN_ASAN_CHECK)
+	      ++asan_num_accesses;
+	  }
+    }
+
+  bool use_calls = ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD < INT_MAX
+    && asan_num_accesses >= ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD;
+
+  FOR_EACH_BB_FN (bb, fun)
+    {
+      gimple_stmt_iterator gsi;
+      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
+	{
+	  gimple stmt = gsi_stmt (gsi);
+	  bool no_next = false;
+
+	  if (!is_gimple_call (stmt))
+	    {
+	      gsi_next (&gsi);
+	      continue;
+	    }
+
+	  if (gimple_call_internal_p (stmt))
+	    {
+	      enum internal_fn ifn = gimple_call_internal_fn (stmt);
+	      switch (ifn)
+		{
+		case IFN_UBSAN_NULL:
+		  no_next = ubsan_expand_null_ifn (&gsi);
+		  break;
+		case IFN_UBSAN_BOUNDS:
+		  no_next = ubsan_expand_bounds_ifn (&gsi);
+		  break;
+		case IFN_UBSAN_OBJECT_SIZE:
+		  no_next = ubsan_expand_objsize_ifn (&gsi);
+		  break;
+		case IFN_ASAN_CHECK:
+		  no_next = asan_expand_check_ifn (&gsi, use_calls);
+		  break;
+		default:
+		  break;
+		}
+	    }
+
+	  if (dump_file && (dump_flags & TDF_DETAILS))
+	    {
+	      fprintf (dump_file, "Expanded\n  ");
+	      print_gimple_stmt (dump_file, stmt, 0, dump_flags);
+	      fprintf (dump_file, "\n");
+	    }
+
+	  if (!no_next)
+	    gsi_next (&gsi);
+	}
+    }
+  return 0;
+}
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_sanopt (gcc::context *ctxt)
+{
+  return new pass_sanopt (ctxt);
+}
diff --git gcc/testsuite/c-c++-common/ubsan/align-2.c gcc/testsuite/c-c++-common/ubsan/align-2.c
index 071de8c..02a26e2 100644
--- gcc/testsuite/c-c++-common/ubsan/align-2.c
+++ gcc/testsuite/c-c++-common/ubsan/align-2.c
@@ -51,6 +51,4 @@ main ()
 /* { dg-output "\.c:(13|16):\[0-9]*: \[^\n\r]*store to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" } */
 /* { dg-output "\.c:23:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
 /* { dg-output "\.c:(29|30):\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
-/* { dg-output "\.c:30:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
-/* { dg-output "\.c:31:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
 /* { dg-output "\.c:37:\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'long long int', which requires \[48] byte alignment" } */
diff --git gcc/testsuite/c-c++-common/ubsan/align-4.c gcc/testsuite/c-c++-common/ubsan/align-4.c
index 3252595..f010589 100644
--- gcc/testsuite/c-c++-common/ubsan/align-4.c
+++ gcc/testsuite/c-c++-common/ubsan/align-4.c
@@ -9,6 +9,4 @@
 /* { dg-output "\[^\n\r]*\.c:(13|16):\[0-9]*: \[^\n\r]*store to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" } */
 /* { dg-output "\[^\n\r]*\.c:23:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
 /* { dg-output "\[^\n\r]*\.c:(29|30):\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
-/* { dg-output "\[^\n\r]*\.c:30:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
-/* { dg-output "\[^\n\r]*\.c:31:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
 /* { dg-output "\[^\n\r]*\.c:37:\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'long long int', which requires \[48] byte alignment" } */
diff --git gcc/testsuite/g++.dg/ubsan/null-1.C gcc/testsuite/g++.dg/ubsan/null-1.C
index e1524b1..83b3033 100644
--- gcc/testsuite/g++.dg/ubsan/null-1.C
+++ gcc/testsuite/g++.dg/ubsan/null-1.C
@@ -25,6 +25,4 @@ main (void)
 }
 
 // { dg-output "reference binding to null pointer of type 'int'(\n|\r\n|\r)" }
-// { dg-output "\[^\n\r]*reference binding to null pointer of type 'int'(\n|\r\n|\r)" }
 // { dg-output "\[^\n\r]*reference binding to null pointer of type 'const L'(\n|\r\n|\r)" }
-// { dg-output "\[^\n\r]*reference binding to null pointer of type 'int'(\n|\r\n|\r)" }
diff --git gcc/testsuite/g++.dg/ubsan/null-2.C gcc/testsuite/g++.dg/ubsan/null-2.C
index 88f387e..0230c7c 100644
--- gcc/testsuite/g++.dg/ubsan/null-2.C
+++ gcc/testsuite/g++.dg/ubsan/null-2.C
@@ -35,5 +35,3 @@ main (void)
 
 // { dg-output "\.C:26:\[0-9]*:\[\^\n\r]*member call on null pointer of type 'struct U'.*" }
 // { dg-output "\.C:29:\[0-9]*:\[\^\n\r]*member call on null pointer of type 'struct V'.*" }
-// { dg-output "\.C:31:\[0-9]*:\[\^\n\r]*member call on null pointer of type 'struct V'.*" }
-// { dg-output "\.C:33:\[0-9]*:\[\^\n\r]*member call on null pointer of type 'struct V'" }

	Marek

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize UBSAN_NULL checks, add sanopt.c
  2014-11-04 18:36   ` Marek Polacek
@ 2014-11-04 19:18     ` Jakub Jelinek
  0 siblings, 0 replies; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-04 19:18 UTC (permalink / raw)
  To: Marek Polacek; +Cc: GCC Patches

On Tue, Nov 04, 2014 at 07:36:26PM +0100, Marek Polacek wrote:
> +		  FOR_EACH_VEC_ELT_REVERSE (v, i, g)
> +		    {
> +		      /* Remove statements for BBs that have been
> +			 already processed.  */
> +		      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
> +		      if (si->visited_p)
> +			{
> +			  v.unordered_remove (i);
> +			  continue;
> +			}
> +		      /* At this point we shouldn't have any statements
> +			 that aren't dominating the current BB.  */
> +		      tree align = gimple_call_arg (g, 2);
> +		      remove = tree_int_cst_le (cur_align, align);
> +		      break;
> +		    }

As you only remove last item, that is pop.
So, how about

	while (!v.is_empty ())
	  {
	    gimple g = v.last ();
	    /* Remove statements for BBs that have been
	       already processed.  */
	    sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
	    if (si->visited_p)
	      v.pop ();
	    else
	      {
		/* At this point we shouldn't have any statements
		   that aren't dominating the current BB.  */
		tree align = gimple_call_arg (g, 2);
		remove = tree_int_cst_le (cur_align, align);
		break;
	      }
	  }
?

The patch is ok either way.

	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize UBSAN_NULL checks, add sanopt.c
  2014-11-03 14:28 [PATCH] Optimize UBSAN_NULL checks, add sanopt.c Marek Polacek
  2014-11-03 15:35 ` Jakub Jelinek
@ 2014-11-05  9:19 ` Yury Gribov
  2014-11-05  9:33   ` Jakub Jelinek
  1 sibling, 1 reply; 49+ messages in thread
From: Yury Gribov @ 2014-11-05  9:19 UTC (permalink / raw)
  To: Marek Polacek, GCC Patches, Jakub Jelinek

On 11/03/2014 05:27 PM, Marek Polacek wrote:
> Another shot at optimizing redundant UBSAN_NULL statements.
>
> This time we walk the dominator tree - that should result in
> more effective optimization - and keep a list of UBSAN_NULL
> statements that dominate the current block, see the comment
> before sanopt_optimize_walker.

Marek,

A general question - have you considered coding this as a dataflow loop 
instead of dominator walk?  That would allow to also remove checks for 
variables defined via PHI nodes provided that all arguments of PHI have 
already been checked.

-Y

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize UBSAN_NULL checks, add sanopt.c
  2014-11-05  9:19 ` Yury Gribov
@ 2014-11-05  9:33   ` Jakub Jelinek
  2014-11-05  9:54     ` Yury Gribov
  0 siblings, 1 reply; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-05  9:33 UTC (permalink / raw)
  To: Yury Gribov; +Cc: Marek Polacek, GCC Patches

On Wed, Nov 05, 2014 at 12:19:22PM +0300, Yury Gribov wrote:
> On 11/03/2014 05:27 PM, Marek Polacek wrote:
> >Another shot at optimizing redundant UBSAN_NULL statements.
> >
> >This time we walk the dominator tree - that should result in
> >more effective optimization - and keep a list of UBSAN_NULL
> >statements that dominate the current block, see the comment
> >before sanopt_optimize_walker.
> 
> Marek,
> 
> A general question - have you considered coding this as a dataflow loop
> instead of dominator walk?  That would allow to also remove checks for
> variables defined via PHI nodes provided that all arguments of PHI have
> already been checked.

I'd be afraid that we'd turn sanopt into another var-tracking that way,
with possibly huge hash tables being copied on write, merging of the tables
etc., with big memory and time requirements, having to add --param limits
to give up if the sum size of the tables go over certain limit.  Or can you
explain how this problem is different from the var-tracking problem?

The way Marek has coded it up is pretty cheap optimization.

BTW, as discussed privately with Marek last time, we probably want to
optimize UBSAN_NULL (etc.) only if -fno-sanitize-recover=null (etc.)
or if location_t is the same, otherwise such optimizations lead to only
one problem being reported instead of all of them.

	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize UBSAN_NULL checks, add sanopt.c
  2014-11-05  9:33   ` Jakub Jelinek
@ 2014-11-05  9:54     ` Yury Gribov
  2014-11-05 10:29       ` Marek Polacek
  0 siblings, 1 reply; 49+ messages in thread
From: Yury Gribov @ 2014-11-05  9:54 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Marek Polacek, GCC Patches

On 11/05/2014 12:33 PM, Jakub Jelinek wrote:
> On Wed, Nov 05, 2014 at 12:19:22PM +0300, Yury Gribov wrote:
>> On 11/03/2014 05:27 PM, Marek Polacek wrote:
>>> Another shot at optimizing redundant UBSAN_NULL statements.
>>>
>>> This time we walk the dominator tree - that should result in
>>> more effective optimization - and keep a list of UBSAN_NULL
>>> statements that dominate the current block, see the comment
>>> before sanopt_optimize_walker.
>>
>> Marek,
>>
>> A general question - have you considered coding this as a dataflow loop
>> instead of dominator walk?  That would allow to also remove checks for
>> variables defined via PHI nodes provided that all arguments of PHI have
>> already been checked.
>
> I'd be afraid that we'd turn sanopt into another var-tracking that way,
> with possibly huge hash tables being copied on write, merging of the tables
> etc., with big memory and time requirements, having to add --param limits
> to give up if the sum size of the tables go over certain limit.

Sure, that would be slower.  I was just curious whether you considered 
alternatives (looks like you did).

> The way Marek has coded it up is pretty cheap optimization.

Right.

> BTW, as discussed privately with Marek last time, we probably want to
> optimize UBSAN_NULL (etc.) only if -fno-sanitize-recover=null (etc.)
> or if location_t is the same, otherwise such optimizations lead to only
> one problem being reported instead of all of them.

Are you going to work on ASan soon?  I could rebase my patches on top of 
Marek's infrastructure.

-Y

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize UBSAN_NULL checks, add sanopt.c
  2014-11-05  9:54     ` Yury Gribov
@ 2014-11-05 10:29       ` Marek Polacek
  2014-11-05 10:50         ` Jakub Jelinek
  0 siblings, 1 reply; 49+ messages in thread
From: Marek Polacek @ 2014-11-05 10:29 UTC (permalink / raw)
  To: Yury Gribov; +Cc: Jakub Jelinek, GCC Patches

On Wed, Nov 05, 2014 at 12:54:37PM +0300, Yury Gribov wrote:
> Are you going to work on ASan soon?  I could rebase my patches on top of
> Marek's infrastructure.

I'm not going to work on ASan today or tomorrow, but it'd be nice to
get this ASan opt in in this stage1.

So if you can rebase your patch, I think that will be appreciated.

	Marek

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize UBSAN_NULL checks, add sanopt.c
  2014-11-05 10:29       ` Marek Polacek
@ 2014-11-05 10:50         ` Jakub Jelinek
  2014-11-05 11:23           ` Marek Polacek
  2014-11-11 17:49           ` [RFC PATCH] Optimize ASAN_CHECK checks Jakub Jelinek
  0 siblings, 2 replies; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-05 10:50 UTC (permalink / raw)
  To: Marek Polacek; +Cc: Yury Gribov, GCC Patches

On Wed, Nov 05, 2014 at 11:29:19AM +0100, Marek Polacek wrote:
> On Wed, Nov 05, 2014 at 12:54:37PM +0300, Yury Gribov wrote:
> > Are you going to work on ASan soon?  I could rebase my patches on top of
> > Marek's infrastructure.
> 
> I'm not going to work on ASan today or tomorrow, but it'd be nice to
> get this ASan opt in in this stage1.
> 
> So if you can rebase your patch, I think that will be appreciated.

Note, the algorithm we were discussing with Honza for the
"is there any possibility of a freeing call on the path between a dominating
and dominated ASAN_CHECK"
problem was to compute it lazily; have flags for asan per-bb:
1) bb might contain a !nonfreeing_call_p
2) there is a bb with flag 1) set in some path between imm(bb) and bb
3) flag whether 2) has been computed already
4) some temporary "being visited" flag
and the algorithm:
1) when walking a bb, if you encounter a !nonfreeing_call_p call, either
   immediately nuke recorded earlier ASAN_CHECKs from the current bb,
   or use gimple_uids for lazily doing that; but in any case, record
   the flag 1) for the current bb
2) if you are considering ASAN_CHECK in a different bb than ASAN_CHECK
   it is dominating, check the 2) flag on the current bb, then on
   get_immediate_dominator (bb) etc. until you reach the bb with the
   dominating bb, if the 2) flag is set on any of them, don't optimize;
   if the 2) flag is not computed on any of these (i.e. flag 3) unset),
   then compute it recursively; set the 4) flag on a bb, for incoming
   edges if the src bb is not the imm(bb) of the original bb, and does
   not have 4) flag set: if it has 1) set, use 1, if it has 3) flag set,
   use 2), otherwise recurse (and or the result); unset 4) flag before
   returning; or so.

For tsan, pretty much the same thing, just with different 1)/2)/3)
flags and different test for that (instead of !nonfreeing_call_p
we are interested in: uses atomics or calls that might use atomics
or other pthread_* synchronization primitives).

	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize UBSAN_NULL checks, add sanopt.c
  2014-11-05 10:50         ` Jakub Jelinek
@ 2014-11-05 11:23           ` Marek Polacek
  2014-11-05 12:16             ` Yury Gribov
  2014-11-11 17:49           ` [RFC PATCH] Optimize ASAN_CHECK checks Jakub Jelinek
  1 sibling, 1 reply; 49+ messages in thread
From: Marek Polacek @ 2014-11-05 11:23 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Yury Gribov, GCC Patches

On Wed, Nov 05, 2014 at 11:50:20AM +0100, Jakub Jelinek wrote:
> On Wed, Nov 05, 2014 at 11:29:19AM +0100, Marek Polacek wrote:
> > On Wed, Nov 05, 2014 at 12:54:37PM +0300, Yury Gribov wrote:
> > > Are you going to work on ASan soon?  I could rebase my patches on top of
> > > Marek's infrastructure.
> > 
> > I'm not going to work on ASan today or tomorrow, but it'd be nice to
> > get this ASan opt in in this stage1.
> > 
> > So if you can rebase your patch, I think that will be appreciated.
> 
> Note, the algorithm we were discussing with Honza for the
> "is there any possibility of a freeing call on the path between a dominating
> and dominated ASAN_CHECK"

Right.  Let me see then if I can implement the following soon, maybe
it makes sense to rebase Yuri's patch only on top of this algorithm.

> problem was to compute it lazily; have flags for asan per-bb:
> 1) bb might contain a !nonfreeing_call_p
> 2) there is a bb with flag 1) set in some path between imm(bb) and bb
> 3) flag whether 2) has been computed already
> 4) some temporary "being visited" flag
> and the algorithm:
> 1) when walking a bb, if you encounter a !nonfreeing_call_p call, either
>    immediately nuke recorded earlier ASAN_CHECKs from the current bb,
>    or use gimple_uids for lazily doing that; but in any case, record
>    the flag 1) for the current bb
> 2) if you are considering ASAN_CHECK in a different bb than ASAN_CHECK
>    it is dominating, check the 2) flag on the current bb, then on
>    get_immediate_dominator (bb) etc. until you reach the bb with the
>    dominating bb, if the 2) flag is set on any of them, don't optimize;
>    if the 2) flag is not computed on any of these (i.e. flag 3) unset),
>    then compute it recursively; set the 4) flag on a bb, for incoming
>    edges if the src bb is not the imm(bb) of the original bb, and does
>    not have 4) flag set: if it has 1) set, use 1, if it has 3) flag set,
>    use 2), otherwise recurse (and or the result); unset 4) flag before
>    returning; or so.
> 
> For tsan, pretty much the same thing, just with different 1)/2)/3)
> flags and different test for that (instead of !nonfreeing_call_p
> we are interested in: uses atomics or calls that might use atomics
> or other pthread_* synchronization primitives).

	Marek

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize UBSAN_NULL checks, add sanopt.c
  2014-11-05 11:23           ` Marek Polacek
@ 2014-11-05 12:16             ` Yury Gribov
  2014-11-05 12:22               ` Jakub Jelinek
  0 siblings, 1 reply; 49+ messages in thread
From: Yury Gribov @ 2014-11-05 12:16 UTC (permalink / raw)
  To: Marek Polacek, Jakub Jelinek; +Cc: GCC Patches

On 11/05/2014 02:23 PM, Marek Polacek wrote:
> On Wed, Nov 05, 2014 at 11:50:20AM +0100, Jakub Jelinek wrote:
>> On Wed, Nov 05, 2014 at 11:29:19AM +0100, Marek Polacek wrote:
>>> On Wed, Nov 05, 2014 at 12:54:37PM +0300, Yury Gribov wrote:
>>>> Are you going to work on ASan soon?  I could rebase my patches on top of
>>>> Marek's infrastructure.
>>>
>>> I'm not going to work on ASan today or tomorrow, but it'd be nice to
>>> get this ASan opt in in this stage1.
>>>
>>> So if you can rebase your patch, I think that will be appreciated.
>>
>> Note, the algorithm we were discussing with Honza for the
>> "is there any possibility of a freeing call on the path between a dominating
>> and dominated ASAN_CHECK"
>
> Right.  Let me see then if I can implement the following soon, maybe
> it makes sense to rebase Yuri's patch only on top of this algorithm.

The algorithm looks like should_hoist_expr_to_dom in gcse.c btw.

BTW have you considered relaxing the non-freeing restriction to not drop 
accesses to globals and stack variables? I wonder if we could win 
something there.

-Y

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize UBSAN_NULL checks, add sanopt.c
  2014-11-05 12:16             ` Yury Gribov
@ 2014-11-05 12:22               ` Jakub Jelinek
  2014-11-05 12:34                 ` Yury Gribov
  0 siblings, 1 reply; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-05 12:22 UTC (permalink / raw)
  To: Yury Gribov; +Cc: Marek Polacek, GCC Patches

On Wed, Nov 05, 2014 at 03:16:49PM +0300, Yury Gribov wrote:
> On 11/05/2014 02:23 PM, Marek Polacek wrote:
> >On Wed, Nov 05, 2014 at 11:50:20AM +0100, Jakub Jelinek wrote:
> >>On Wed, Nov 05, 2014 at 11:29:19AM +0100, Marek Polacek wrote:
> >>>On Wed, Nov 05, 2014 at 12:54:37PM +0300, Yury Gribov wrote:
> >>>>Are you going to work on ASan soon?  I could rebase my patches on top of
> >>>>Marek's infrastructure.
> >>>
> >>>I'm not going to work on ASan today or tomorrow, but it'd be nice to
> >>>get this ASan opt in in this stage1.
> >>>
> >>>So if you can rebase your patch, I think that will be appreciated.
> >>
> >>Note, the algorithm we were discussing with Honza for the
> >>"is there any possibility of a freeing call on the path between a dominating
> >>and dominated ASAN_CHECK"
> >
> >Right.  Let me see then if I can implement the following soon, maybe
> >it makes sense to rebase Yuri's patch only on top of this algorithm.
> 
> The algorithm looks like should_hoist_expr_to_dom in gcse.c btw.
> 
> BTW have you considered relaxing the non-freeing restriction to not drop
> accesses to globals and stack variables? I wonder if we could win something
> there.

Wouldn't it break most uses of __asan_poison_memory_region ?

	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize UBSAN_NULL checks, add sanopt.c
  2014-11-05 12:22               ` Jakub Jelinek
@ 2014-11-05 12:34                 ` Yury Gribov
  2014-11-05 13:13                   ` Yury Gribov
  0 siblings, 1 reply; 49+ messages in thread
From: Yury Gribov @ 2014-11-05 12:34 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Marek Polacek, GCC Patches

On 11/05/2014 03:21 PM, Jakub Jelinek wrote:
> On Wed, Nov 05, 2014 at 03:16:49PM +0300, Yury Gribov wrote:
>> On 11/05/2014 02:23 PM, Marek Polacek wrote:
>>> On Wed, Nov 05, 2014 at 11:50:20AM +0100, Jakub Jelinek wrote:
>>>> On Wed, Nov 05, 2014 at 11:29:19AM +0100, Marek Polacek wrote:
>>>>> On Wed, Nov 05, 2014 at 12:54:37PM +0300, Yury Gribov wrote:
>>>>>> Are you going to work on ASan soon?  I could rebase my patches on top of
>>>>>> Marek's infrastructure.
>>>>>
>>>>> I'm not going to work on ASan today or tomorrow, but it'd be nice to
>>>>> get this ASan opt in in this stage1.
>>>>>
>>>>> So if you can rebase your patch, I think that will be appreciated.
>>>>
>>>> Note, the algorithm we were discussing with Honza for the
>>>> "is there any possibility of a freeing call on the path between a dominating
>>>> and dominated ASAN_CHECK"
>>>
>>> Right.  Let me see then if I can implement the following soon, maybe
>>> it makes sense to rebase Yuri's patch only on top of this algorithm.
>>
>> The algorithm looks like should_hoist_expr_to_dom in gcse.c btw.
>>
>> BTW have you considered relaxing the non-freeing restriction to not drop
>> accesses to globals and stack variables? I wonder if we could win something
>> there.
>
> Wouldn't it break most uses of __asan_poison_memory_region ?

Most probably but I wonder if we should ask people to simply do asm 
volatile with memory clobber in this case?  And we probably shouldn't 
call the whole thing is_nonfreeing anyway.

-Y

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize UBSAN_NULL checks, add sanopt.c
  2014-11-05 12:34                 ` Yury Gribov
@ 2014-11-05 13:13                   ` Yury Gribov
  2014-11-05 13:23                     ` Jakub Jelinek
  0 siblings, 1 reply; 49+ messages in thread
From: Yury Gribov @ 2014-11-05 13:13 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Marek Polacek, GCC Patches, Konstantin Serebryany

On 11/05/2014 03:34 PM, Yury Gribov wrote:
> On 11/05/2014 03:21 PM, Jakub Jelinek wrote:
>> On Wed, Nov 05, 2014 at 03:16:49PM +0300, Yury Gribov wrote:
>>> On 11/05/2014 02:23 PM, Marek Polacek wrote:
>>>> On Wed, Nov 05, 2014 at 11:50:20AM +0100, Jakub Jelinek wrote:
>>>>> On Wed, Nov 05, 2014 at 11:29:19AM +0100, Marek Polacek wrote:
>>>>>> On Wed, Nov 05, 2014 at 12:54:37PM +0300, Yury Gribov wrote:
>>>>>>> Are you going to work on ASan soon?  I could rebase my patches on
>>>>>>> top of
>>>>>>> Marek's infrastructure.
>>>>>>
>>>>>> I'm not going to work on ASan today or tomorrow, but it'd be nice to
>>>>>> get this ASan opt in in this stage1.
>>>>>>
>>>>>> So if you can rebase your patch, I think that will be appreciated.
>>>>>
>>>>> Note, the algorithm we were discussing with Honza for the
>>>>> "is there any possibility of a freeing call on the path between a
>>>>> dominating
>>>>> and dominated ASAN_CHECK"
>>>>
>>>> Right.  Let me see then if I can implement the following soon, maybe
>>>> it makes sense to rebase Yuri's patch only on top of this algorithm.
>>>
>>> The algorithm looks like should_hoist_expr_to_dom in gcse.c btw.
>>>
>>> BTW have you considered relaxing the non-freeing restriction to not drop
>>> accesses to globals and stack variables? I wonder if we could win
>>> something
>>> there.
>>
>> Wouldn't it break most uses of __asan_poison_memory_region ?
>
> Most probably but I wonder if we should ask people to simply do asm
> volatile with memory clobber in this case?  And we probably shouldn't
> call the whole thing is_nonfreeing anyway.

Added Kostya to maybe comment on this.

-Y

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize UBSAN_NULL checks, add sanopt.c
  2014-11-05 13:13                   ` Yury Gribov
@ 2014-11-05 13:23                     ` Jakub Jelinek
  2014-11-05 13:48                       ` Yury Gribov
  0 siblings, 1 reply; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-05 13:23 UTC (permalink / raw)
  To: Yury Gribov; +Cc: Marek Polacek, GCC Patches, Konstantin Serebryany

On Wed, Nov 05, 2014 at 04:13:01PM +0300, Yury Gribov wrote:
> >>Wouldn't it break most uses of __asan_poison_memory_region ?
> >
> >Most probably but I wonder if we should ask people to simply do asm
> >volatile with memory clobber in this case?  And we probably shouldn't
> >call the whole thing is_nonfreeing anyway.
> 
> Added Kostya to maybe comment on this.

Well, right now !nonfreeing_call_p is any non-builtin call or
non-leaf builtin call (i.e. builtin that might call functions in the
current CU), or free/tm_free/realloc/stack_restore.
So, by this definition __asan_poison_memory_region is also a
!nonfreeing_call_p.  Where would you like to see the volatile with
memory clobber?  You might very well just call some function that
does the free for you.
  *p = 1;
  foo (p);
  *p = 2;
and foo (p) could asm volatile ("" : : : "memory"); somewhere
and free (p) somewhere else.

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), things
would still work unless you LTOed libasan together with your app
(to make that work you'd probably want to add asm volatile to
those calls, but doing it now would be very premature, or
make __asan_*poison_memory_region a builtin that would be handled
explicitly).

Anyway, what I mean, ATM most of the calls are still going to be considered
possibly freeing, and it will be pretty much the same calls that are
considered as potentially calling __asan_poison_memory_region, so the
optimization wouldn't change much.

	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize UBSAN_NULL checks, add sanopt.c
  2014-11-05 13:23                     ` Jakub Jelinek
@ 2014-11-05 13:48                       ` Yury Gribov
  2014-11-12  9:52                         ` Maxim Ostapenko
  0 siblings, 1 reply; 49+ messages in thread
From: Yury Gribov @ 2014-11-05 13:48 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Marek Polacek, GCC Patches, Konstantin Serebryany

On 11/05/2014 04:23 PM, Jakub Jelinek wrote:
> On Wed, Nov 05, 2014 at 04:13:01PM +0300, Yury Gribov wrote:
>>>> Wouldn't it break most uses of __asan_poison_memory_region ?
>>>
>>> Most probably but I wonder if we should ask people to simply do asm
>>> volatile with memory clobber in this case?  And we probably shouldn't
>>> call the whole thing is_nonfreeing anyway.
>>
>> Added Kostya to maybe comment on this.
>
> Well, right now !nonfreeing_call_p is any non-builtin call or
> non-leaf builtin call (i.e. builtin that might call functions in the
> current CU), or free/tm_free/realloc/stack_restore.
> So, by this definition __asan_poison_memory_region is also a
> !nonfreeing_call_p.  Where would you like to see the volatile with
> memory clobber?
 >
> You might very well just call some function that
> does the free for you.
>    *p = 1;
>    foo (p);
>    *p = 2;
> and foo (p) could asm volatile ("" : : : "memory"); somewhere
> and free (p) somewhere else.

I was thinking about e.g. removing check for the second access in

extern int x[];

void foo (int i) {
     x[i] = 1;
     foo (p);
     x[i] = 2;
}

because accessability of &a[i] obviously can't be changed by any call to 
free () inside foo ().  But you are probably right that 
__asan_poison_memory could potentially be called inside foo (however 
rare it is) which would preclude this sort of optimization.

> 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.

-Y

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [RFC PATCH] Optimize ASAN_CHECK checks
  2014-11-05 10:50         ` Jakub Jelinek
  2014-11-05 11:23           ` Marek Polacek
@ 2014-11-11 17:49           ` Jakub Jelinek
  2014-11-12  9:26             ` Yury Gribov
  1 sibling, 1 reply; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-11 17:49 UTC (permalink / raw)
  To: Yury Gribov, Jan Hubicka; +Cc: Marek Polacek, GCC Patches

On Wed, Nov 05, 2014 at 11:50:20AM +0100, Jakub Jelinek wrote:
> On Wed, Nov 05, 2014 at 11:29:19AM +0100, Marek Polacek wrote:
> > On Wed, Nov 05, 2014 at 12:54:37PM +0300, Yury Gribov wrote:
> > > Are you going to work on ASan soon?  I could rebase my patches on top of
> > > Marek's infrastructure.
> > 
> > I'm not going to work on ASan today or tomorrow, but it'd be nice to
> > get this ASan opt in in this stage1.
> > 
> > So if you can rebase your patch, I think that will be appreciated.
> 
> Note, the algorithm we were discussing with Honza for the
> "is there any possibility of a freeing call on the path between a dominating
> and dominated ASAN_CHECK"
> problem was to compute it lazily; have flags for asan per-bb:
> 1) bb might contain a !nonfreeing_call_p
> 2) there is a bb with flag 1) set in some path between imm(bb) and bb
> 3) flag whether 2) has been computed already
> 4) some temporary "being visited" flag
> and the algorithm:
> 1) when walking a bb, if you encounter a !nonfreeing_call_p call, either
>    immediately nuke recorded earlier ASAN_CHECKs from the current bb,
>    or use gimple_uids for lazily doing that; but in any case, record
>    the flag 1) for the current bb
> 2) if you are considering ASAN_CHECK in a different bb than ASAN_CHECK
>    it is dominating, check the 2) flag on the current bb, then on
>    get_immediate_dominator (bb) etc. until you reach the bb with the
>    dominating bb, if the 2) flag is set on any of them, don't optimize;
>    if the 2) flag is not computed on any of these (i.e. flag 3) unset),
>    then compute it recursively; set the 4) flag on a bb, for incoming
>    edges if the src bb is not the imm(bb) of the original bb, and does
>    not have 4) flag set: if it has 1) set, use 1, if it has 3) flag set,
>    use 2), otherwise recurse (and or the result); unset 4) flag before
>    returning; or so.

Here is a patch (partly written by Marek) implementing that algorithm,
only very lightly tested beyond
make -j16 -k check RUNTESTFLAGS="--target_board=\{-m32,-m64\} asan.exp ubsan.exp tsan.exp"

Honza, do you think you could look over the algorithm if it is sane?
I couldn't decide if it is ok to cache negative results for the
imm_dom_path_with_freeing_call_p flag (i.e. set
imm_dom_path_with_freeing_call_computed_p and keep
imm_dom_path_with_freeing_call_p cleared) for basic blocks encountered
during the recursion, if they have different immediate dominator than
the bb I've called the recursive function for originally, and during
the search some being_visited_p flag has been encountered.

I've been playing with testcase like:

int b[64], b2[128];
void bar (void);

int
foo (int *p, int *q, int x, int y)
{
  int v = *(char *) p;
  __builtin_memcpy (b, b2, 17);
  int w = (*p)++;
  if (x)
    *p = 6;
  int z = *q;
  if (y)
    bar ();
  *q = 8;
  return v + w + z;
}

int
baz (int *p, int x)
{
  int v = *p;
  int i, j = 0;
  for (i = 0; i < 64; i++)
    if (b[i]++ < 20)
      b2[i] += i < 76 ? ({asm ("" : "+r" (j)); 0;}) : i * 4;
  v += *p;
  for (i = 0; i < 64; i++)
    if (b[i]++ < 20)
      b2[i] += i < 76 ? ({asm ("" : "+r" (j)); 0;}) : i * 4;
    else if (b2[i] > 17)
      bar ();
  v += *p;
  return v;
}

but guess that isn't sufficient for proper test coverage.

--- gcc/asan.c.jj	2014-11-11 00:06:18.000000000 +0100
+++ gcc/asan.c	2014-11-11 11:42:29.327583317 +0100
@@ -299,15 +299,6 @@ static GTY(()) tree shadow_ptr_types[2];
 /* Decl for __asan_option_detect_stack_use_after_return.  */
 static GTY(()) tree asan_detect_stack_use_after_return;
 
-/* Various flags for Asan builtins.  */
-enum asan_check_flags
-{
-  ASAN_CHECK_STORE = 1 << 0,
-  ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
-  ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
-  ASAN_CHECK_LAST = 1 << 3
-};
-
 /* Hashtable support for memory references used by gimple
    statements.  */
 
--- gcc/asan.h.jj	2014-11-11 00:06:18.000000000 +0100
+++ gcc/asan.h	2014-11-11 11:43:24.999578043 +0100
@@ -59,6 +59,15 @@ extern alias_set_type asan_shadow_set;
 #define ASAN_STACK_FRAME_MAGIC		0x41b58ab3
 #define ASAN_STACK_RETIRED_MAGIC	0x45e0360e
 
+/* Various flags for Asan builtins.  */
+enum asan_check_flags
+{
+  ASAN_CHECK_STORE = 1 << 0,
+  ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
+  ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
+  ASAN_CHECK_LAST = 1 << 3
+};
+
 /* Return true if DECL should be guarded on the stack.  */
 
 static inline bool
--- gcc/gimple.c.jj	2014-11-11 00:06:21.000000000 +0100
+++ gcc/gimple.c	2014-11-11 10:02:17.385736225 +0100
@@ -2538,6 +2538,9 @@ nonfreeing_call_p (gimple call)
 	default:
 	  return true;
       }
+  else if (gimple_call_internal_p (call)
+	   && gimple_call_flags (call) & ECF_LEAF)
+    return true;
 
   return false;
 }
--- gcc/sanopt.c.jj	2014-11-11 09:13:36.698280115 +0100
+++ gcc/sanopt.c	2014-11-11 18:07:17.913539517 +0100
@@ -49,6 +49,7 @@ along with GCC; see the file COPYING3.
 #include "langhooks.h"
 #include "ubsan.h"
 #include "params.h"
+#include "tree-ssa-operands.h"
 
 
 /* This is used to carry information about basic blocks.  It is
@@ -56,7 +57,29 @@ along with GCC; see the file COPYING3.
 
 struct sanopt_info
 {
-  /* True if this BB has been visited.  */
+  /* True if this BB might call (directly or indirectly) free/munmap
+     or similar operation.  */
+  bool has_freeing_call_p;
+
+  /* True if HAS_FREEING_CALL_P flag has been computed.  */
+  bool has_freeing_call_computed_p;
+
+  /* True if there is a block with HAS_FREEING_CALL_P flag set
+     on any a path between imm(BB) and BB.  */
+  bool imm_dom_path_with_freeing_call_p;
+
+  /* True if IMM_DOM_PATH_WITH_FREEING_CALL_P has been computed.  */
+  bool imm_dom_path_with_freeing_call_computed_p;
+
+  /* Number of possibly freeing calls encountered in this bb
+     (so far).  */
+  uint64_t freeing_call_events;
+
+  /* True if BB is currently being visited during computation
+     of IMM_DOM_PATH_WITH_FREEING_CALL_P flag.  */
+  bool being_visited_p;
+
+  /* True if this BB has been visited in the dominator walk.  */
   bool visited_p;
 };
 
@@ -69,11 +92,307 @@ struct sanopt_ctx
      a vector of UBSAN_NULL call statements that check this pointer.  */
   hash_map<tree, auto_vec<gimple> > null_check_map;
 
+  /* This map maps a pointer (the second argument of ASAN_CHECK) to
+     a vector of ASAN_CHECK call statements that check the access.  */
+  hash_map<tree, auto_vec<gimple> > asan_check_map;
+
   /* Number of IFN_ASAN_CHECK statements.  */
   int asan_num_accesses;
 };
 
 
+/* Return true if there might be any call to free/munmap operation
+   on any path in between DOM (which should be imm(BB)) and BB.  */
+
+static bool
+imm_dom_path_with_freeing_call (basic_block bb, basic_block dom)
+{
+  sanopt_info *info = (sanopt_info *) bb->aux;
+  edge e;
+  edge_iterator ei;
+
+  if (info->imm_dom_path_with_freeing_call_computed_p)
+    return info->imm_dom_path_with_freeing_call_p;
+
+  info->being_visited_p = true;
+
+  FOR_EACH_EDGE (e, ei, bb->preds)
+    {
+      sanopt_info *pred_info = (sanopt_info *) e->src->aux;
+
+      if (e->src == dom)
+	continue;
+
+      if ((pred_info->imm_dom_path_with_freeing_call_computed_p
+	  && pred_info->imm_dom_path_with_freeing_call_p)
+	  || (pred_info->has_freeing_call_computed_p
+	      && pred_info->has_freeing_call_p))
+	{
+	  info->imm_dom_path_with_freeing_call_computed_p = true;
+	  info->imm_dom_path_with_freeing_call_p = true;
+	  info->being_visited_p = false;
+	  return true;
+	}
+    }
+
+  FOR_EACH_EDGE (e, ei, bb->preds)
+    {
+      sanopt_info *pred_info = (sanopt_info *) e->src->aux;
+
+      if (e->src == dom)
+	continue;
+
+      if (pred_info->has_freeing_call_computed_p)
+	continue;
+
+      gimple_stmt_iterator gsi;
+      for (gsi = gsi_start_bb (e->src); !gsi_end_p (gsi); gsi_next (&gsi))
+	{
+	  gimple stmt = gsi_stmt (gsi);
+
+	  if (is_gimple_call (stmt) && !nonfreeing_call_p (stmt))
+	    {
+	      pred_info->has_freeing_call_p = true;
+	      break;
+	    }
+	}
+
+      pred_info->has_freeing_call_computed_p = true;
+      if (pred_info->has_freeing_call_p)
+	{
+	  info->imm_dom_path_with_freeing_call_computed_p = true;
+	  info->imm_dom_path_with_freeing_call_p = true;
+	  info->being_visited_p = false;
+	  return true;
+	}
+    }
+
+  FOR_EACH_EDGE (e, ei, bb->preds)
+    {
+      if (e->src == dom)
+	continue;
+
+      basic_block src;
+      for (src = e->src; src != dom; )
+	{
+	  sanopt_info *pred_info = (sanopt_info *) src->aux;
+	  if (pred_info->being_visited_p)
+	    break;
+	  basic_block imm = get_immediate_dominator (CDI_DOMINATORS, src);
+	  if (imm_dom_path_with_freeing_call (src, imm))
+	    {
+	      info->imm_dom_path_with_freeing_call_computed_p = true;
+	      info->imm_dom_path_with_freeing_call_p = true;
+	      info->being_visited_p = false;
+	      return true;
+	    }
+	  src = imm;
+	}
+    }
+
+  info->imm_dom_path_with_freeing_call_computed_p = true;
+  info->imm_dom_path_with_freeing_call_p = false;
+  info->being_visited_p = false;
+  return false;
+}
+
+/* Optimize away redundant UBSAN_NULL calls.  */
+
+static bool
+maybe_optimize_ubsan_null_ifn (struct sanopt_ctx *ctx, gimple stmt)
+{
+  gcc_assert (gimple_call_num_args (stmt) == 3);
+  tree ptr = gimple_call_arg (stmt, 0);
+  tree cur_align = gimple_call_arg (stmt, 2);
+  gcc_assert (TREE_CODE (cur_align) == INTEGER_CST);
+  bool remove = false;
+
+  auto_vec<gimple> &v = ctx->null_check_map.get_or_insert (ptr);
+  if (v.is_empty ())
+    {
+      /* For this PTR we don't have any UBSAN_NULL stmts recorded, so there's
+	 nothing to optimize yet.  */
+      v.safe_push (stmt);
+      return false;
+    }
+
+  /* We already have recorded a UBSAN_NULL check for this pointer. Perhaps we
+     can drop this one.  But only if this check doesn't specify stricter
+     alignment.  */
+  while (!v.is_empty ())
+    {
+      gimple g = v.last ();
+      /* Remove statements for BBs that have been already processed.  */
+      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
+      if (si->visited_p)
+	{
+	  v.pop ();
+	  continue;
+	}
+
+      /* At this point we shouldn't have any statements that aren't dominating
+	 the current BB.  */
+      tree align = gimple_call_arg (g, 2);
+      int kind = tree_to_shwi (gimple_call_arg (g, 1));
+      /* If this is a NULL pointer check where we had segv anyway, we can
+	 remove it.  */
+      if (integer_zerop (align)
+	  && (kind == UBSAN_LOAD_OF
+	      || kind == UBSAN_STORE_OF
+	      || kind == UBSAN_MEMBER_ACCESS))
+	remove = true;
+      /* Otherwise remove the check in non-recovering mode, or if the
+	 stmts have same location.  */
+      else if (integer_zerop (align))
+	remove = (flag_sanitize_recover & SANITIZE_NULL) == 0
+		 || flag_sanitize_undefined_trap_on_error
+		 || gimple_location (g) == gimple_location (stmt);
+      else if (tree_int_cst_le (cur_align, align))
+	remove = (flag_sanitize_recover & SANITIZE_ALIGNMENT) == 0
+		 || flag_sanitize_undefined_trap_on_error
+		 || gimple_location (g) == gimple_location (stmt);
+      if (!remove && gimple_bb (g) == gimple_bb (stmt)
+	  && tree_int_cst_compare (cur_align, align) == 0)
+	v.pop ();
+      break;
+    }
+
+  if (!remove)
+    v.safe_push (stmt);
+  return remove;
+}
+
+/* Optimize away redundant ASAN_CHECK calls.  */
+
+static bool
+maybe_optimize_asan_check_ifn (struct sanopt_ctx *ctx, gimple stmt)
+{
+  gcc_assert (gimple_call_num_args (stmt) == 4);
+  int flags = tree_to_uhwi (gimple_call_arg (stmt, 0));
+  tree ptr = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
+  basic_block bb = gimple_bb (stmt);
+  sanopt_info *info = (sanopt_info *) bb->aux;
+
+  if (TREE_CODE (len) != INTEGER_CST
+      && (flags & ASAN_CHECK_NON_ZERO_LEN) == 0)
+    return false;
+  if (integer_zerop (len))
+    return false;
+
+  gimple_set_uid (stmt, info->freeing_call_events);
+
+  auto_vec<gimple> &v = ctx->asan_check_map.get_or_insert (ptr);
+  if (v.is_empty ())
+    {
+      /* For this PTR we don't have any ASAN_CHECK stmts recorded, so there's
+	 nothing to optimize yet.  */
+      v.safe_push (stmt);
+      return false;
+    }
+
+  /* We already have recorded a ASAN_CHECK check for this pointer.  Perhaps
+     we can drop this one.  But only if this check doesn't specify larger
+     size.  */
+  while (!v.is_empty ())
+    {
+      gimple g = v.last ();
+      /* Remove statements for BBs that have been already processed.  */
+      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
+      if (si->visited_p)
+	v.pop ();
+      else
+	break;
+    }
+
+  unsigned int i;
+  gimple g;
+  gimple to_pop = NULL;
+  bool remove = false;
+  basic_block last_bb = bb;
+  bool cleanup = false;
+
+  FOR_EACH_VEC_ELT_REVERSE (v, i, g)
+    {
+      basic_block gbb = gimple_bb (g);
+      sanopt_info *si = (sanopt_info *) gbb->aux;
+      if (gimple_uid (g) < si->freeing_call_events)
+	{
+	  /* If there is a potentially freeing call after g in gbb, we should
+	     remove it from the vector, can't use in optimization.  */
+	  cleanup = true;
+	  continue;
+	}
+
+      if (TREE_CODE (len) != INTEGER_CST)
+	{
+	  /* If there is some stmts not followed by freeing call event
+	     for ptr already in the current bb, no need to insert anything.
+	     Non-constant len is treated just as length 1.  */
+	  if (gbb == bb)
+	    return false;
+	  break;
+	}
+
+      tree glen = gimple_call_arg (g, 2);
+      /* If glen is not integer, we'd have added it to the vector only if
+	 ASAN_CHECK_NON_ZERO_LEN flag is set, so treat it as length 1.  */
+      if (TREE_CODE (glen) != INTEGER_CST)
+	glen = integer_one_node;
+      /* If we've checked only smaller length than we want to check now,
+	 we can't remove the current stmt.  If g is in the same basic block,
+	 we want to remove it though, as the current stmt is better.  */
+      if (tree_int_cst_lt (glen, len))
+	{
+	  if (gbb == bb)
+	    {
+	      to_pop = g;
+	      cleanup = true;
+	    }
+	  continue;
+	}
+
+      while (last_bb != gbb)
+	{
+	  /* Paths from last_bb to bb have been checked before.
+	     gbb is necessarily a dominator of last_bb, but not necessarily
+	     immediate dominator.  */
+	  if (((sanopt_info *) last_bb->aux)->freeing_call_events)
+	    break;
+
+	  basic_block imm = get_immediate_dominator (CDI_DOMINATORS, last_bb);
+	  gcc_assert (imm);
+	  if (imm_dom_path_with_freeing_call (last_bb, imm))
+	    break;
+
+	  last_bb = imm;
+	}
+      if (last_bb == gbb)
+	remove = true;
+      break;
+    }
+
+  if (cleanup)
+    {
+      unsigned int j = 0, l = v.length ();
+      for (i = 0; i < l; i++)
+	if (v[i] != to_pop
+	    && (gimple_uid (v[i])
+		== ((sanopt_info *)
+		    gimple_bb (v[i])->aux)->freeing_call_events))
+	  {
+	    if (i != j)
+	      v[j] = v[i];
+	    j++;
+	  }
+      v.truncate (j);
+    }
+
+  if (!remove)
+    v.safe_push (stmt);
+  return remove;
+}
+
 /* Try to optimize away redundant UBSAN_NULL checks.
    
    We walk blocks in the CFG via a depth first search of the dominator
@@ -89,111 +408,77 @@ sanopt_optimize_walker (basic_block bb,
 {
   basic_block son;
   gimple_stmt_iterator gsi;
+  sanopt_info *info = (sanopt_info *) bb->aux;
+  bool asan_check_optimize
+    = (flag_sanitize & SANITIZE_ADDRESS)
+      && ((flag_sanitize & flag_sanitize_recover
+	   & SANITIZE_KERNEL_ADDRESS) == 0);
 
   for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
     {
       gimple stmt = gsi_stmt (gsi);
       bool remove = false;
 
-      if (is_gimple_call (stmt)
-	  && gimple_call_internal_p (stmt))
+      if (!is_gimple_call (stmt))
+	{
+	  /* Handle asm volatile or asm with "memory" clobber
+	     the same as potentionally freeing call.  */
+	  if (gimple_code (stmt) == GIMPLE_ASM
+	      && asan_check_optimize
+	      && (gimple_asm_clobbers_memory_p (stmt)
+		  || gimple_asm_volatile_p (stmt)))
+	    info->freeing_call_events++;
+	  gsi_next (&gsi);
+	  continue;
+	}
+
+      if (asan_check_optimize && !nonfreeing_call_p (stmt))
+	info->freeing_call_events++;
+
+      if (gimple_call_internal_p (stmt))
 	switch (gimple_call_internal_fn (stmt))
 	  {
 	  case IFN_UBSAN_NULL:
-	    {
-	      gcc_assert (gimple_call_num_args (stmt) == 3);
-	      tree ptr = gimple_call_arg (stmt, 0);
-	      tree cur_align = gimple_call_arg (stmt, 2);
-	      gcc_assert (TREE_CODE (cur_align) == INTEGER_CST);
-
-	      auto_vec<gimple> &v = ctx->null_check_map.get_or_insert (ptr);
-	      if (v.is_empty ())
-		/* For this PTR we don't have any UBSAN_NULL stmts
-		   recorded, so there's nothing to optimize yet.  */
-		v.safe_push (stmt);
-	      else
-		{
-		  /* We already have recorded a UBSAN_NULL check
-		     for this pointer.  Perhaps we can drop this one.
-		     But only if this check doesn't specify stricter
-		     alignment.  */
-		  while (!v.is_empty ())
-		    {
-		      gimple g = v.last ();
-		      /* Remove statements for BBs that have been
-			 already processed.  */
-		      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
-		      if (si->visited_p)
-			v.pop ();
-		      else
-			{
-			  /* At this point we shouldn't have any statements
-			     that aren't dominating the current BB.  */
-			  tree align = gimple_call_arg (g, 2);
-			  int kind = tree_to_shwi (gimple_call_arg (g, 1));
-			  /* If this is a NULL pointer check where we had segv
-			     anyway, we can remove it.  */
-			  if (integer_zerop (align)
-			      && (kind == UBSAN_LOAD_OF
-				  || kind == UBSAN_STORE_OF
-				  || kind == UBSAN_MEMBER_ACCESS))
-			    remove = true;
-			  /* Otherwise remove the check in non-recovering
-			     mode, or if the stmts have same location.  */
-			  else if (integer_zerop (align))
-			    remove = !(flag_sanitize_recover & SANITIZE_NULL)
-				     || flag_sanitize_undefined_trap_on_error
-				     || gimple_location (g)
-					== gimple_location (stmt);
-			  else if (tree_int_cst_le (cur_align, align))
-			    remove = !(flag_sanitize_recover
-				       & SANITIZE_ALIGNMENT)
-				     || flag_sanitize_undefined_trap_on_error
-				     || gimple_location (g)
-					== gimple_location (stmt);
-			  if (!remove && gimple_bb (g) == gimple_bb (stmt)
-			      && tree_int_cst_compare (cur_align, align) == 0)
-			    v.pop ();
-			  break;
-			}
-		    }
-
-		  if (remove)
-		    {
-		      /* Drop this check.  */
-		      if (dump_file && (dump_flags & TDF_DETAILS))
-			{
-			  fprintf (dump_file, "Optimizing out\n  ");
-			  print_gimple_stmt (dump_file, stmt, 0,
-					     dump_flags);
-			  fprintf (dump_file, "\n");
-			}
-		      gsi_remove (&gsi, true);
-		    }
-		  else
-		    v.safe_push (stmt);
-		  }
-	    }
+	    remove = maybe_optimize_ubsan_null_ifn (ctx, stmt);
+	    break;
 	  case IFN_ASAN_CHECK:
-	    ctx->asan_num_accesses++;
+	    if (asan_check_optimize)
+	      remove = maybe_optimize_asan_check_ifn (ctx, stmt);
+	    if (!remove)
+	      ctx->asan_num_accesses++;
 	    break;
 	  default:
 	    break;
 	  }
 
-      /* If we were able to remove the current statement, gsi_remove
-	 already pointed us to the next statement.  */
-      if (!remove)
+      if (remove)
+	{
+	  /* Drop this check.  */
+	  if (dump_file && (dump_flags & TDF_DETAILS))
+	    {
+	      fprintf (dump_file, "Optimizing out\n  ");
+	      print_gimple_stmt (dump_file, stmt, 0, dump_flags);
+	      fprintf (dump_file, "\n");
+	    }
+	  unlink_stmt_vdef (stmt);
+	  gsi_remove (&gsi, true);
+	}
+      else
 	gsi_next (&gsi);
     }
 
+  if (asan_check_optimize)
+    {
+      info->has_freeing_call_p = info->freeing_call_events != 0;
+      info->has_freeing_call_computed_p = true;
+    }
+
   for (son = first_dom_son (CDI_DOMINATORS, bb);
        son;
        son = next_dom_son (CDI_DOMINATORS, son))
     sanopt_optimize_walker (son, ctx);
 
   /* We're leaving this BB, so mark it to that effect.  */
-  sanopt_info *info = (sanopt_info *) bb->aux;
   info->visited_p = true;
 }
 
@@ -259,7 +544,8 @@ pass_sanopt::execute (function *fun)
 
   /* Try to remove redundant checks.  */
   if (optimize
-      && (flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT)))
+      && (flag_sanitize
+	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_ADDRESS)))
     asan_num_accesses = sanopt_optimize (fun);
   else if (flag_sanitize & SANITIZE_ADDRESS)
     {


	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [RFC PATCH] Optimize ASAN_CHECK checks
  2014-11-11 17:49           ` [RFC PATCH] Optimize ASAN_CHECK checks Jakub Jelinek
@ 2014-11-12  9:26             ` Yury Gribov
  2014-11-12 10:35               ` Jakub Jelinek
  0 siblings, 1 reply; 49+ messages in thread
From: Yury Gribov @ 2014-11-12  9:26 UTC (permalink / raw)
  To: Jakub Jelinek, Jan Hubicka; +Cc: Marek Polacek, GCC Patches

On 11/11/2014 08:42 PM, Jakub Jelinek wrote:
> On Wed, Nov 05, 2014 at 11:50:20AM +0100, Jakub Jelinek wrote:
>> On Wed, Nov 05, 2014 at 11:29:19AM +0100, Marek Polacek wrote:
>>> On Wed, Nov 05, 2014 at 12:54:37PM +0300, Yury Gribov wrote:
>>>> Are you going to work on ASan soon?  I could rebase my patches on top of
>>>> Marek's infrastructure.
>>>
>>> I'm not going to work on ASan today or tomorrow, but it'd be nice to
>>> get this ASan opt in in this stage1.
>>>
>>> So if you can rebase your patch, I think that will be appreciated.
>>
>> Note, the algorithm we were discussing with Honza for the
>> "is there any possibility of a freeing call on the path between a dominating
>> and dominated ASAN_CHECK"
>> problem was to compute it lazily; have flags for asan per-bb:
>> 1) bb might contain a !nonfreeing_call_p
>> 2) there is a bb with flag 1) set in some path between imm(bb) and bb
>> 3) flag whether 2) has been computed already
>> 4) some temporary "being visited" flag
>> and the algorithm:
>> 1) when walking a bb, if you encounter a !nonfreeing_call_p call, either
>>     immediately nuke recorded earlier ASAN_CHECKs from the current bb,
>>     or use gimple_uids for lazily doing that; but in any case, record
>>     the flag 1) for the current bb
>> 2) if you are considering ASAN_CHECK in a different bb than ASAN_CHECK
>>     it is dominating, check the 2) flag on the current bb, then on
>>     get_immediate_dominator (bb) etc. until you reach the bb with the
>>     dominating bb, if the 2) flag is set on any of them, don't optimize;
>>     if the 2) flag is not computed on any of these (i.e. flag 3) unset),
>>     then compute it recursively; set the 4) flag on a bb, for incoming
>>     edges if the src bb is not the imm(bb) of the original bb, and does
>>     not have 4) flag set: if it has 1) set, use 1, if it has 3) flag set,
>>     use 2), otherwise recurse (and or the result); unset 4) flag before
>>     returning; or so.
>
> Here is a patch (partly written by Marek) implementing that algorithm,
> only very lightly tested beyond
> make -j16 -k check RUNTESTFLAGS="--target_board=\{-m32,-m64\} asan.exp ubsan.exp tsan.exp"

Yeah, would be interesting to see how many checks it removes from 
Asan-bootstrapped GCC.

> Honza, do you think you could look over the algorithm if it is sane?
> I couldn't decide if it is ok to cache negative results for the
> imm_dom_path_with_freeing_call_p flag (i.e. set
> imm_dom_path_with_freeing_call_computed_p and keep
> imm_dom_path_with_freeing_call_p cleared) for basic blocks encountered
> during the recursion, if they have different immediate dominator than
> the bb I've called the recursive function for originally, and during
> the search some being_visited_p flag has been encountered.
>
> I've been playing with testcase like:
>
> int b[64], b2[128];
> void bar (void);
>
> int
> foo (int *p, int *q, int x, int y)
> {
>    int v = *(char *) p;
>    __builtin_memcpy (b, b2, 17);
>    int w = (*p)++;
>    if (x)
>      *p = 6;
>    int z = *q;
>    if (y)
>      bar ();
>    *q = 8;
>    return v + w + z;
> }
>
> int
> baz (int *p, int x)
> {
>    int v = *p;
>    int i, j = 0;
>    for (i = 0; i < 64; i++)
>      if (b[i]++ < 20)
>        b2[i] += i < 76 ? ({asm ("" : "+r" (j)); 0;}) : i * 4;
>    v += *p;
>    for (i = 0; i < 64; i++)
>      if (b[i]++ < 20)
>        b2[i] += i < 76 ? ({asm ("" : "+r" (j)); 0;}) : i * 4;
>      else if (b2[i] > 17)
>        bar ();
>    v += *p;
>    return v;
> }
>
> but guess that isn't sufficient for proper test coverage.
>
> --- gcc/asan.c.jj	2014-11-11 00:06:18.000000000 +0100
> +++ gcc/asan.c	2014-11-11 11:42:29.327583317 +0100
> @@ -299,15 +299,6 @@ static GTY(()) tree shadow_ptr_types[2];
>   /* Decl for __asan_option_detect_stack_use_after_return.  */
>   static GTY(()) tree asan_detect_stack_use_after_return;
>
> -/* Various flags for Asan builtins.  */
> -enum asan_check_flags
> -{
> -  ASAN_CHECK_STORE = 1 << 0,
> -  ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
> -  ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
> -  ASAN_CHECK_LAST = 1 << 3
> -};
> -
>   /* Hashtable support for memory references used by gimple
>      statements.  */
>
> --- gcc/asan.h.jj	2014-11-11 00:06:18.000000000 +0100
> +++ gcc/asan.h	2014-11-11 11:43:24.999578043 +0100
> @@ -59,6 +59,15 @@ extern alias_set_type asan_shadow_set;
>   #define ASAN_STACK_FRAME_MAGIC		0x41b58ab3
>   #define ASAN_STACK_RETIRED_MAGIC	0x45e0360e
>
> +/* Various flags for Asan builtins.  */
> +enum asan_check_flags
> +{
> +  ASAN_CHECK_STORE = 1 << 0,
> +  ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
> +  ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
> +  ASAN_CHECK_LAST = 1 << 3
> +};
> +
>   /* Return true if DECL should be guarded on the stack.  */
>
>   static inline bool
> --- gcc/gimple.c.jj	2014-11-11 00:06:21.000000000 +0100
> +++ gcc/gimple.c	2014-11-11 10:02:17.385736225 +0100
> @@ -2538,6 +2538,9 @@ nonfreeing_call_p (gimple call)
>   	default:
>   	  return true;
>         }
> +  else if (gimple_call_internal_p (call)
> +	   && gimple_call_flags (call) & ECF_LEAF)
> +    return true;

Nice.

>
>     return false;
>   }
> --- gcc/sanopt.c.jj	2014-11-11 09:13:36.698280115 +0100
> +++ gcc/sanopt.c	2014-11-11 18:07:17.913539517 +0100
> @@ -49,6 +49,7 @@ along with GCC; see the file COPYING3.
>   #include "langhooks.h"
>   #include "ubsan.h"
>   #include "params.h"
> +#include "tree-ssa-operands.h"
>
>
>   /* This is used to carry information about basic blocks.  It is
> @@ -56,7 +57,29 @@ along with GCC; see the file COPYING3.
>
>   struct sanopt_info
>   {
> -  /* True if this BB has been visited.  */
> +  /* True if this BB might call (directly or indirectly) free/munmap
> +     or similar operation.  */

Frankly I think this is more about memory poisoned status then free.

> +  bool has_freeing_call_p;
> +
> +  /* True if HAS_FREEING_CALL_P flag has been computed.  */
> +  bool has_freeing_call_computed_p;
> +
> +  /* True if there is a block with HAS_FREEING_CALL_P flag set
> +     on any a path between imm(BB) and BB.  */
> +  bool imm_dom_path_with_freeing_call_p;
> +
> +  /* True if IMM_DOM_PATH_WITH_FREEING_CALL_P has been computed.  */
> +  bool imm_dom_path_with_freeing_call_computed_p;
> +
> +  /* Number of possibly freeing calls encountered in this bb
> +     (so far).  */
> +  uint64_t freeing_call_events;
> +
> +  /* True if BB is currently being visited during computation
> +     of IMM_DOM_PATH_WITH_FREEING_CALL_P flag.  */
> +  bool being_visited_p;
> +
> +  /* True if this BB has been visited in the dominator walk.  */
>     bool visited_p;
>   };
>
> @@ -69,11 +92,307 @@ struct sanopt_ctx
>        a vector of UBSAN_NULL call statements that check this pointer.  */
>     hash_map<tree, auto_vec<gimple> > null_check_map;
>
> +  /* This map maps a pointer (the second argument of ASAN_CHECK) to
> +     a vector of ASAN_CHECK call statements that check the access.  */
> +  hash_map<tree, auto_vec<gimple> > asan_check_map;

How about using traits class like the one below for both maps?

struct tree_map_traits : default_hashmap_traits
{
   static inline hashval_t hash (const_tree ref)
     {
       return iterative_hash_expr (ref, 0);
     }

   static inline bool equal_keys (const_tree ref1, const_tree ref2)
     {
       return operand_equal_p (ref1, ref2, 0);
     }
};

Also the hash_map probably deserves a typedef.

> +
>     /* Number of IFN_ASAN_CHECK statements.  */
>     int asan_num_accesses;
>   };
>
>
> +/* Return true if there might be any call to free/munmap operation
> +   on any path in between DOM (which should be imm(BB)) and BB.  */
> +
> +static bool
> +imm_dom_path_with_freeing_call (basic_block bb, basic_block dom)

Perhaps some parameter to bound search in pathalogical cases?

> +{
> +  sanopt_info *info = (sanopt_info *) bb->aux;
> +  edge e;
> +  edge_iterator ei;
> +
> +  if (info->imm_dom_path_with_freeing_call_computed_p)
> +    return info->imm_dom_path_with_freeing_call_p;
> +
> +  info->being_visited_p = true;
> +
> +  FOR_EACH_EDGE (e, ei, bb->preds)
> +    {
> +      sanopt_info *pred_info = (sanopt_info *) e->src->aux;
> +
> +      if (e->src == dom)
> +	continue;
> +
> +      if ((pred_info->imm_dom_path_with_freeing_call_computed_p
> +	  && pred_info->imm_dom_path_with_freeing_call_p)
> +	  || (pred_info->has_freeing_call_computed_p
> +	      && pred_info->has_freeing_call_p))
> +	{
> +	  info->imm_dom_path_with_freeing_call_computed_p = true;
> +	  info->imm_dom_path_with_freeing_call_p = true;
> +	  info->being_visited_p = false;
> +	  return true;
> +	}
> +    }
> +
> +  FOR_EACH_EDGE (e, ei, bb->preds)
> +    {
> +      sanopt_info *pred_info = (sanopt_info *) e->src->aux;
> +
> +      if (e->src == dom)
> +	continue;
> +
> +      if (pred_info->has_freeing_call_computed_p)
> +	continue;
> +
> +      gimple_stmt_iterator gsi;
> +      for (gsi = gsi_start_bb (e->src); !gsi_end_p (gsi); gsi_next (&gsi))
> +	{
> +	  gimple stmt = gsi_stmt (gsi);
> +
> +	  if (is_gimple_call (stmt) && !nonfreeing_call_p (stmt))
> +	    {
> +	      pred_info->has_freeing_call_p = true;
> +	      break;
> +	    }
> +	}
> +
> +      pred_info->has_freeing_call_computed_p = true;
> +      if (pred_info->has_freeing_call_p)
> +	{
> +	  info->imm_dom_path_with_freeing_call_computed_p = true;
> +	  info->imm_dom_path_with_freeing_call_p = true;
> +	  info->being_visited_p = false;
> +	  return true;
> +	}
> +    }
> +
> +  FOR_EACH_EDGE (e, ei, bb->preds)
> +    {
> +      if (e->src == dom)
> +	continue;
> +
> +      basic_block src;
> +      for (src = e->src; src != dom; )
> +	{
> +	  sanopt_info *pred_info = (sanopt_info *) src->aux;
> +	  if (pred_info->being_visited_p)
> +	    break;
> +	  basic_block imm = get_immediate_dominator (CDI_DOMINATORS, src);
> +	  if (imm_dom_path_with_freeing_call (src, imm))
> +	    {
> +	      info->imm_dom_path_with_freeing_call_computed_p = true;
> +	      info->imm_dom_path_with_freeing_call_p = true;
> +	      info->being_visited_p = false;
> +	      return true;
> +	    }
> +	  src = imm;
> +	}
> +    }
> +
> +  info->imm_dom_path_with_freeing_call_computed_p = true;
> +  info->imm_dom_path_with_freeing_call_p = false;
> +  info->being_visited_p = false;
> +  return false;
> +}
> +
> +/* Optimize away redundant UBSAN_NULL calls.  */
> +
> +static bool
> +maybe_optimize_ubsan_null_ifn (struct sanopt_ctx *ctx, gimple stmt)
> +{
> +  gcc_assert (gimple_call_num_args (stmt) == 3);
> +  tree ptr = gimple_call_arg (stmt, 0);
> +  tree cur_align = gimple_call_arg (stmt, 2);
> +  gcc_assert (TREE_CODE (cur_align) == INTEGER_CST);
> +  bool remove = false;
> +
> +  auto_vec<gimple> &v = ctx->null_check_map.get_or_insert (ptr);
> +  if (v.is_empty ())
> +    {
> +      /* For this PTR we don't have any UBSAN_NULL stmts recorded, so there's
> +	 nothing to optimize yet.  */
> +      v.safe_push (stmt);
> +      return false;
> +    }
> +
> +  /* We already have recorded a UBSAN_NULL check for this pointer. Perhaps we
> +     can drop this one.  But only if this check doesn't specify stricter
> +     alignment.  */
> +  while (!v.is_empty ())
> +    {
> +      gimple g = v.last ();
> +      /* Remove statements for BBs that have been already processed.  */
> +      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
> +      if (si->visited_p)
> +	{
> +	  v.pop ();
> +	  continue;
> +	}
> +
> +      /* At this point we shouldn't have any statements that aren't dominating
> +	 the current BB.  */
> +      tree align = gimple_call_arg (g, 2);
> +      int kind = tree_to_shwi (gimple_call_arg (g, 1));
> +      /* If this is a NULL pointer check where we had segv anyway, we can
> +	 remove it.  */
> +      if (integer_zerop (align)
> +	  && (kind == UBSAN_LOAD_OF
> +	      || kind == UBSAN_STORE_OF
> +	      || kind == UBSAN_MEMBER_ACCESS))
> +	remove = true;
> +      /* Otherwise remove the check in non-recovering mode, or if the
> +	 stmts have same location.  */
> +      else if (integer_zerop (align))
> +	remove = (flag_sanitize_recover & SANITIZE_NULL) == 0
> +		 || flag_sanitize_undefined_trap_on_error
> +		 || gimple_location (g) == gimple_location (stmt);
> +      else if (tree_int_cst_le (cur_align, align))
> +	remove = (flag_sanitize_recover & SANITIZE_ALIGNMENT) == 0
> +		 || flag_sanitize_undefined_trap_on_error
> +		 || gimple_location (g) == gimple_location (stmt);
> +      if (!remove && gimple_bb (g) == gimple_bb (stmt)
> +	  && tree_int_cst_compare (cur_align, align) == 0)
> +	v.pop ();
> +      break;
> +    }
> +
> +  if (!remove)
> +    v.safe_push (stmt);
> +  return remove;
> +}
> +
> +/* Optimize away redundant ASAN_CHECK calls.  */
> +
> +static bool
> +maybe_optimize_asan_check_ifn (struct sanopt_ctx *ctx, gimple stmt)
> +{
> +  gcc_assert (gimple_call_num_args (stmt) == 4);
> +  int flags = tree_to_uhwi (gimple_call_arg (stmt, 0));
> +  tree ptr = gimple_call_arg (stmt, 1);
> +  tree len = gimple_call_arg (stmt, 2);
> +  basic_block bb = gimple_bb (stmt);
> +  sanopt_info *info = (sanopt_info *) bb->aux;
> +
> +  if (TREE_CODE (len) != INTEGER_CST
> +      && (flags & ASAN_CHECK_NON_ZERO_LEN) == 0)
> +    return false;
> +  if (integer_zerop (len))
> +    return false;
> +
> +  gimple_set_uid (stmt, info->freeing_call_events);
> +
> +  auto_vec<gimple> &v = ctx->asan_check_map.get_or_insert (ptr);
> +  if (v.is_empty ())
> +    {
> +      /* For this PTR we don't have any ASAN_CHECK stmts recorded, so there's
> +	 nothing to optimize yet.  */
> +      v.safe_push (stmt);
> +      return false;
> +    }
> +
> +  /* We already have recorded a ASAN_CHECK check for this pointer.  Perhaps
> +     we can drop this one.  But only if this check doesn't specify larger
> +     size.  */
> +  while (!v.is_empty ())
> +    {
> +      gimple g = v.last ();
> +      /* Remove statements for BBs that have been already processed.  */
> +      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
> +      if (si->visited_p)
> +	v.pop ();
> +      else
> +	break;
> +    }

This part seems to be duplicated across maybe_optimize_ubsan_null_ifn 
and maybe_optimize_asan_check_ifn.  Perhaps make some 
maybe_get_dominating_check API?

> +
> +  unsigned int i;
> +  gimple g;
> +  gimple to_pop = NULL;
> +  bool remove = false;
> +  basic_block last_bb = bb;
> +  bool cleanup = false;
> +
> +  FOR_EACH_VEC_ELT_REVERSE (v, i, g)
> +    {
> +      basic_block gbb = gimple_bb (g);
> +      sanopt_info *si = (sanopt_info *) gbb->aux;
> +      if (gimple_uid (g) < si->freeing_call_events)
> +	{
> +	  /* If there is a potentially freeing call after g in gbb, we should
> +	     remove it from the vector, can't use in optimization.  */
> +	  cleanup = true;
> +	  continue;
> +	}
> +
> +      if (TREE_CODE (len) != INTEGER_CST)
> +	{
> +	  /* If there is some stmts not followed by freeing call event
> +	     for ptr already in the current bb, no need to insert anything.
> +	     Non-constant len is treated just as length 1.  */
> +	  if (gbb == bb)
> +	    return false;
> +	  break;
> +	}
> +
> +      tree glen = gimple_call_arg (g, 2);
> +      /* If glen is not integer, we'd have added it to the vector only if
> +	 ASAN_CHECK_NON_ZERO_LEN flag is set, so treat it as length 1.  */

Frankly I don't think we use ASAN_CHECK_NON_ZERO_LEN anymore (it's only 
set for trivial cases now).  Perhaps we should just nuke it from asan.c 
and sanopt.c alltogether?

> +      if (TREE_CODE (glen) != INTEGER_CST)

That's a matter of taste but why not a higher-level tree_fits_shwi and 
tree_to_shwi?

> +	glen = integer_one_node;
> +      /* If we've checked only smaller length than we want to check now,
> +	 we can't remove the current stmt.  If g is in the same basic block,
> +	 we want to remove it though, as the current stmt is better.  */
> +      if (tree_int_cst_lt (glen, len))
> +	{
> +	  if (gbb == bb)
> +	    {
> +	      to_pop = g;
> +	      cleanup = true;
> +	    }
> +	  continue;
> +	}
> +
> +      while (last_bb != gbb)
> +	{
> +	  /* Paths from last_bb to bb have been checked before.
> +	     gbb is necessarily a dominator of last_bb, but not necessarily
> +	     immediate dominator.  */
> +	  if (((sanopt_info *) last_bb->aux)->freeing_call_events)
> +	    break;
> +
> +	  basic_block imm = get_immediate_dominator (CDI_DOMINATORS, last_bb);
> +	  gcc_assert (imm);
> +	  if (imm_dom_path_with_freeing_call (last_bb, imm))
> +	    break;
> +
> +	  last_bb = imm;
> +	}
> +      if (last_bb == gbb)
> +	remove = true;
> +      break;
> +    }
> +
> +  if (cleanup)
> +    {
> +      unsigned int j = 0, l = v.length ();
> +      for (i = 0; i < l; i++)
> +	if (v[i] != to_pop
> +	    && (gimple_uid (v[i])
> +		== ((sanopt_info *)
> +		    gimple_bb (v[i])->aux)->freeing_call_events))
> +	  {
> +	    if (i != j)
> +	      v[j] = v[i];
> +	    j++;
> +	  }
> +      v.truncate (j);
> +    }
> +
> +  if (!remove)
> +    v.safe_push (stmt);
> +  return remove;
> +}
> +
>   /* Try to optimize away redundant UBSAN_NULL checks.
>
>      We walk blocks in the CFG via a depth first search of the dominator
> @@ -89,111 +408,77 @@ sanopt_optimize_walker (basic_block bb,
>   {
>     basic_block son;
>     gimple_stmt_iterator gsi;
> +  sanopt_info *info = (sanopt_info *) bb->aux;

> +  bool asan_check_optimize
> +    = (flag_sanitize & SANITIZE_ADDRESS)
> +      && ((flag_sanitize & flag_sanitize_recover
> +	   & SANITIZE_KERNEL_ADDRESS) == 0);

Why do we disable check optimizations for KASan?

>     for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
>       {
>         gimple stmt = gsi_stmt (gsi);
>         bool remove = false;
>
> -      if (is_gimple_call (stmt)
> -	  && gimple_call_internal_p (stmt))
> +      if (!is_gimple_call (stmt))
> +	{
> +	  /* Handle asm volatile or asm with "memory" clobber
> +	     the same as potentionally freeing call.  */
> +	  if (gimple_code (stmt) == GIMPLE_ASM
> +	      && asan_check_optimize
> +	      && (gimple_asm_clobbers_memory_p (stmt)
> +		  || gimple_asm_volatile_p (stmt)))
> +	    info->freeing_call_events++;
> +	  gsi_next (&gsi);
> +	  continue;
> +	}
> +
> +      if (asan_check_optimize && !nonfreeing_call_p (stmt))
> +	info->freeing_call_events++;
> +
> +      if (gimple_call_internal_p (stmt))
>   	switch (gimple_call_internal_fn (stmt))
>   	  {
>   	  case IFN_UBSAN_NULL:
> -	    {
> -	      gcc_assert (gimple_call_num_args (stmt) == 3);
> -	      tree ptr = gimple_call_arg (stmt, 0);
> -	      tree cur_align = gimple_call_arg (stmt, 2);
> -	      gcc_assert (TREE_CODE (cur_align) == INTEGER_CST);
> -
> -	      auto_vec<gimple> &v = ctx->null_check_map.get_or_insert (ptr);
> -	      if (v.is_empty ())
> -		/* For this PTR we don't have any UBSAN_NULL stmts
> -		   recorded, so there's nothing to optimize yet.  */
> -		v.safe_push (stmt);
> -	      else
> -		{
> -		  /* We already have recorded a UBSAN_NULL check
> -		     for this pointer.  Perhaps we can drop this one.
> -		     But only if this check doesn't specify stricter
> -		     alignment.  */
> -		  while (!v.is_empty ())
> -		    {
> -		      gimple g = v.last ();
> -		      /* Remove statements for BBs that have been
> -			 already processed.  */
> -		      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
> -		      if (si->visited_p)
> -			v.pop ();
> -		      else
> -			{
> -			  /* At this point we shouldn't have any statements
> -			     that aren't dominating the current BB.  */
> -			  tree align = gimple_call_arg (g, 2);
> -			  int kind = tree_to_shwi (gimple_call_arg (g, 1));
> -			  /* If this is a NULL pointer check where we had segv
> -			     anyway, we can remove it.  */
> -			  if (integer_zerop (align)
> -			      && (kind == UBSAN_LOAD_OF
> -				  || kind == UBSAN_STORE_OF
> -				  || kind == UBSAN_MEMBER_ACCESS))
> -			    remove = true;
> -			  /* Otherwise remove the check in non-recovering
> -			     mode, or if the stmts have same location.  */
> -			  else if (integer_zerop (align))
> -			    remove = !(flag_sanitize_recover & SANITIZE_NULL)
> -				     || flag_sanitize_undefined_trap_on_error
> -				     || gimple_location (g)
> -					== gimple_location (stmt);
> -			  else if (tree_int_cst_le (cur_align, align))
> -			    remove = !(flag_sanitize_recover
> -				       & SANITIZE_ALIGNMENT)
> -				     || flag_sanitize_undefined_trap_on_error
> -				     || gimple_location (g)
> -					== gimple_location (stmt);
> -			  if (!remove && gimple_bb (g) == gimple_bb (stmt)
> -			      && tree_int_cst_compare (cur_align, align) == 0)
> -			    v.pop ();
> -			  break;
> -			}
> -		    }
> -
> -		  if (remove)
> -		    {
> -		      /* Drop this check.  */
> -		      if (dump_file && (dump_flags & TDF_DETAILS))
> -			{
> -			  fprintf (dump_file, "Optimizing out\n  ");
> -			  print_gimple_stmt (dump_file, stmt, 0,
> -					     dump_flags);
> -			  fprintf (dump_file, "\n");
> -			}
> -		      gsi_remove (&gsi, true);
> -		    }
> -		  else
> -		    v.safe_push (stmt);
> -		  }
> -	    }
> +	    remove = maybe_optimize_ubsan_null_ifn (ctx, stmt);
> +	    break;
>   	  case IFN_ASAN_CHECK:
> -	    ctx->asan_num_accesses++;
> +	    if (asan_check_optimize)
> +	      remove = maybe_optimize_asan_check_ifn (ctx, stmt);

It may be useful to also store base address in check-table:

static tree
maybe_get_single_definition (tree t)
{
   if (TREE_CODE (t) == SSA_NAME)
     {
       gimple g = SSA_NAME_DEF_STMT (t);
       if (gimple_assign_single_p (g))
         return gimple_assign_rhs1 (g);
     }
   return NULL_TREE;
}

> +	    if (!remove)
> +	      ctx->asan_num_accesses++;
>   	    break;
>   	  default:
>   	    break;
>   	  }
>
> -      /* If we were able to remove the current statement, gsi_remove
> -	 already pointed us to the next statement.  */
> -      if (!remove)
> +      if (remove)
> +	{
> +	  /* Drop this check.  */
> +	  if (dump_file && (dump_flags & TDF_DETAILS))
> +	    {
> +	      fprintf (dump_file, "Optimizing out\n  ");
> +	      print_gimple_stmt (dump_file, stmt, 0, dump_flags);
> +	      fprintf (dump_file, "\n");
> +	    }
> +	  unlink_stmt_vdef (stmt);
> +	  gsi_remove (&gsi, true);
> +	}
> +      else
>   	gsi_next (&gsi);
>       }
>
> +  if (asan_check_optimize)
> +    {
> +      info->has_freeing_call_p = info->freeing_call_events != 0;
> +      info->has_freeing_call_computed_p = true;
> +    }
> +
>     for (son = first_dom_son (CDI_DOMINATORS, bb);
>          son;
>          son = next_dom_son (CDI_DOMINATORS, son))
>       sanopt_optimize_walker (son, ctx);
>
>     /* We're leaving this BB, so mark it to that effect.  */
> -  sanopt_info *info = (sanopt_info *) bb->aux;
>     info->visited_p = true;
>   }
>
> @@ -259,7 +544,8 @@ pass_sanopt::execute (function *fun)
>
>     /* Try to remove redundant checks.  */
>     if (optimize
> -      && (flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT)))
> +      && (flag_sanitize
> +	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_ADDRESS)))
>       asan_num_accesses = sanopt_optimize (fun);
>     else if (flag_sanitize & SANITIZE_ADDRESS)
>       {
>
>
> 	Jakub
>

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize UBSAN_NULL checks, add sanopt.c
  2014-11-05 13:48                       ` Yury Gribov
@ 2014-11-12  9:52                         ` Maxim Ostapenko
  2014-11-12 10:11                           ` Jakub Jelinek
  0 siblings, 1 reply; 49+ messages in thread
From: Maxim Ostapenko @ 2014-11-12  9:52 UTC (permalink / raw)
  To: Yury Gribov, Jakub Jelinek
  Cc: Marek Polacek, GCC Patches, Konstantin Serebryany


>> 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.

-Maxim
> -Y
>

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize UBSAN_NULL checks, add sanopt.c
  2014-11-12  9:52                         ` Maxim Ostapenko
@ 2014-11-12 10:11                           ` Jakub Jelinek
  2014-11-12 11:54                             ` Maxim Ostapenko
  0 siblings, 1 reply; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-12 10:11 UTC (permalink / raw)
  To: Maxim Ostapenko
  Cc: Yury Gribov, Marek Polacek, GCC Patches, Konstantin Serebryany

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

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [RFC PATCH] Optimize ASAN_CHECK checks
  2014-11-12  9:26             ` Yury Gribov
@ 2014-11-12 10:35               ` Jakub Jelinek
  2014-11-12 11:12                 ` Yury Gribov
  2014-11-25 17:26                 ` [PATCH] Enhance ASAN_CHECK optimization Yury Gribov
  0 siblings, 2 replies; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-12 10:35 UTC (permalink / raw)
  To: Yury Gribov; +Cc: Jan Hubicka, Marek Polacek, GCC Patches

On Wed, Nov 12, 2014 at 12:18:31PM +0300, Yury Gribov wrote:
> >--- gcc/sanopt.c.jj	2014-11-11 09:13:36.698280115 +0100
> >+++ gcc/sanopt.c	2014-11-11 18:07:17.913539517 +0100
> >@@ -49,6 +49,7 @@ along with GCC; see the file COPYING3.
> >  #include "langhooks.h"
> >  #include "ubsan.h"
> >  #include "params.h"
> >+#include "tree-ssa-operands.h"
> >
> >
> >  /* This is used to carry information about basic blocks.  It is
> >@@ -56,7 +57,29 @@ along with GCC; see the file COPYING3.
> >
> >  struct sanopt_info
> >  {
> >-  /* True if this BB has been visited.  */
> >+  /* True if this BB might call (directly or indirectly) free/munmap
> >+     or similar operation.  */
> 
> Frankly I think this is more about memory poisoned status then free.

For ASAN about anything that might make ASAN_CHECK non-redundant, that
certainly is free as the most common thing (bet munmap doesn't poison
the memory being unmapped) and also the asan_*poison* calls.

> >@@ -69,11 +92,307 @@ struct sanopt_ctx
> >       a vector of UBSAN_NULL call statements that check this pointer.  */
> >    hash_map<tree, auto_vec<gimple> > null_check_map;
> >
> >+  /* This map maps a pointer (the second argument of ASAN_CHECK) to
> >+     a vector of ASAN_CHECK call statements that check the access.  */
> >+  hash_map<tree, auto_vec<gimple> > asan_check_map;
> 
> How about using traits class like the one below for both maps?

Well, for null_check_map, it is only SSA_NAMEs that matter (perhaps the
code doesn't return early if it is not, maybe should), address of
decls is always non-NULL and alignment should be known too.
Or, Marek, can you see if we can get there e.g. decls for alignments,
extern char a[];
long long int
foo (void)
{
  *(long long int *) &a[0] = 5;
}
?

> struct tree_map_traits : default_hashmap_traits
> {
>   static inline hashval_t hash (const_tree ref)
>     {
>       return iterative_hash_expr (ref, 0);
>     }
> 
>   static inline bool equal_keys (const_tree ref1, const_tree ref2)
>     {
>       return operand_equal_p (ref1, ref2, 0);
>     }
> };
> 
> Also the hash_map probably deserves a typedef.

For asan you're right, we can have addresses of decls there etc.
If you have spare cycles, feel free to take over the patch and adjust it.

> >+/* Return true if there might be any call to free/munmap operation
> >+   on any path in between DOM (which should be imm(BB)) and BB.  */
> >+
> >+static bool
> >+imm_dom_path_with_freeing_call (basic_block bb, basic_block dom)
> 
> Perhaps some parameter to bound search in pathalogical cases?

I believe right now (Honza, correct me if wrong) the algorithm doesn't
have pathological cases.

> >+  /* We already have recorded a ASAN_CHECK check for this pointer.  Perhaps
> >+     we can drop this one.  But only if this check doesn't specify larger
> >+     size.  */
> >+  while (!v.is_empty ())
> >+    {
> >+      gimple g = v.last ();
> >+      /* Remove statements for BBs that have been already processed.  */
> >+      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
> >+      if (si->visited_p)
> >+	v.pop ();
> >+      else
> >+	break;
> >+    }
> 
> This part seems to be duplicated across maybe_optimize_ubsan_null_ifn and
> maybe_optimize_asan_check_ifn.  Perhaps make some maybe_get_dominating_check
> API?

I think they aren't similar to make it worth it.
Though, if we optimize tsan, I'd say we should use exactly the same routines
(and given that tsan is compile time incompatible with asan, can use even
the same flags).  Just, different definition of what is the "freeing call"
thing - bet we need a better name for that.  For asan it is
asm volatile, asm (... : "memory") or a call that might free or asan*poison
or munmap or so.  For tsan, I believe it is asm volatile, asm (... :
"memory") or a __sync_*/__atomic_* call or pthread_* synchronization
primitive, or some function that has any of those in that.  So, for
start !nonfreeing_call_p () || __sync/__atomic* builtin I think.  But for
tsan we'd need to figure out the rules what we can optimize.

> >+      tree glen = gimple_call_arg (g, 2);
> >+      /* If glen is not integer, we'd have added it to the vector only if
> >+	 ASAN_CHECK_NON_ZERO_LEN flag is set, so treat it as length 1.  */
> 
> Frankly I don't think we use ASAN_CHECK_NON_ZERO_LEN anymore (it's only set
> for trivial cases now).  Perhaps we should just nuke it from asan.c and
> sanopt.c alltogether?

I thought that for the builtins libasan doesn't instrument (which includes
very often used functions like __memcpy_chk etc.) we still use it.
> 
> >+      if (TREE_CODE (glen) != INTEGER_CST)
> 
> That's a matter of taste but why not a higher-level tree_fits_shwi and
> tree_to_shwi?

As we compare it only using tree_int_cst_lt, there is no point to require
that it fits into shwi.  Even if it doesn't, we can handle it.

> >+  bool asan_check_optimize
> >+    = (flag_sanitize & SANITIZE_ADDRESS)
> >+      && ((flag_sanitize & flag_sanitize_recover
> >+	   & SANITIZE_KERNEL_ADDRESS) == 0);
> 
> Why do we disable check optimizations for KASan?

Only for -fno-sanitize-recover=kernel-address too.  The thing is,
if you do recover from failed asan checks, supposedly you want to
see all errors reported, not just the first one.

> >  	  case IFN_ASAN_CHECK:
> >-	    ctx->asan_num_accesses++;
> >+	    if (asan_check_optimize)
> >+	      remove = maybe_optimize_asan_check_ifn (ctx, stmt);
> 
> It may be useful to also store base address in check-table:
> 
> static tree
> maybe_get_single_definition (tree t)
> {
>   if (TREE_CODE (t) == SSA_NAME)
>     {
>       gimple g = SSA_NAME_DEF_STMT (t);
>       if (gimple_assign_single_p (g))
>         return gimple_assign_rhs1 (g);
>     }
>   return NULL_TREE;
> }

Why?  forwprop etc. should have propagated it into the ASAN_CHECK if
it is is_gimple_val.  Or do you have specific examples which you have in
mind?

	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [RFC PATCH] Optimize ASAN_CHECK checks
  2014-11-12 10:35               ` Jakub Jelinek
@ 2014-11-12 11:12                 ` Yury Gribov
  2014-11-12 22:41                   ` [PATCH] " Jakub Jelinek
  2014-11-25 17:26                 ` [PATCH] Enhance ASAN_CHECK optimization Yury Gribov
  1 sibling, 1 reply; 49+ messages in thread
From: Yury Gribov @ 2014-11-12 11:12 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Jan Hubicka, Marek Polacek, GCC Patches

On 11/12/2014 01:34 PM, Jakub Jelinek wrote:
> On Wed, Nov 12, 2014 at 12:18:31PM +0300, Yury Gribov wrote:
>>> --- gcc/sanopt.c.jj	2014-11-11 09:13:36.698280115 +0100
>>> +++ gcc/sanopt.c	2014-11-11 18:07:17.913539517 +0100
>>> @@ -49,6 +49,7 @@ along with GCC; see the file COPYING3.
>>>   #include "langhooks.h"
>>>   #include "ubsan.h"
>>>   #include "params.h"
>>> +#include "tree-ssa-operands.h"
>>>
>>>
>>>   /* This is used to carry information about basic blocks.  It is
>>> @@ -56,7 +57,29 @@ along with GCC; see the file COPYING3.
>>>
>>>   struct sanopt_info
>>>   {
>>> -  /* True if this BB has been visited.  */
>>> +  /* True if this BB might call (directly or indirectly) free/munmap
>>> +     or similar operation.  */
>>
>> Frankly I think this is more about memory poisoned status then free.
>
> For ASAN about anything that might make ASAN_CHECK non-redundant, that
> certainly is free as the most common thing (bet munmap doesn't poison
> the memory being unmapped) and also the asan_*poison* calls.

It's all about names, "freeing" is just subset of what really goes on 
("poisoning").  We can keep current "freeing"/"nonfreeing" stuff but 
IMHO it doesn't properly express what we really bother about.

>>> @@ -69,11 +92,307 @@ struct sanopt_ctx
>>>        a vector of UBSAN_NULL call statements that check this pointer.  */
>>>     hash_map<tree, auto_vec<gimple> > null_check_map;
>>>
>>> +  /* This map maps a pointer (the second argument of ASAN_CHECK) to
>>> +     a vector of ASAN_CHECK call statements that check the access.  */
>>> +  hash_map<tree, auto_vec<gimple> > asan_check_map;
>>
>> How about using traits class like the one below for both maps?
>
> Well, for null_check_map, it is only SSA_NAMEs that matter (perhaps the
> code doesn't return early if it is not, maybe should), address of
> decls is always non-NULL and alignment should be known too.
> Or, Marek, can you see if we can get there e.g. decls for alignments,
> extern char a[];
> long long int
> foo (void)
> {
>    *(long long int *) &a[0] = 5;
> }
> ?

See below for maybe_get_single_definition discussion.

>> struct tree_map_traits : default_hashmap_traits
>> {
>>    static inline hashval_t hash (const_tree ref)
>>      {
>>        return iterative_hash_expr (ref, 0);
>>      }
>>
>>    static inline bool equal_keys (const_tree ref1, const_tree ref2)
>>      {
>>        return operand_equal_p (ref1, ref2, 0);
>>      }
>> };
>>
>> Also the hash_map probably deserves a typedef.
>
> For asan you're right, we can have addresses of decls there etc.
> If you have spare cycles, feel free to take over the patch and adjust it.

I guess I'd wait when this gets to trunk?

>>> +      tree glen = gimple_call_arg (g, 2);
>>> +      /* If glen is not integer, we'd have added it to the vector only if
>>> +	 ASAN_CHECK_NON_ZERO_LEN flag is set, so treat it as length 1.  */
>>
>> Frankly I don't think we use ASAN_CHECK_NON_ZERO_LEN anymore (it's only set
>> for trivial cases now).  Perhaps we should just nuke it from asan.c and
>> sanopt.c alltogether?
>
> I thought that for the builtins libasan doesn't instrument (which includes
> very often used functions like __memcpy_chk etc.) we still use it.

We could only emit non-trivial ASAN_CHECK_NON_ZERO_LEN in rare cases 
e.g. checking buffer length _after_ strlen call but I don't think we 
have anything like this left.

>>> +  bool asan_check_optimize
>>> +    = (flag_sanitize & SANITIZE_ADDRESS)
>>> +      && ((flag_sanitize & flag_sanitize_recover
>>> +	   & SANITIZE_KERNEL_ADDRESS) == 0);
>>
>> Why do we disable check optimizations for KASan?
>
> Only for -fno-sanitize-recover=kernel-address too.  The thing is,
> if you do recover from failed asan checks, supposedly you want to
> see all errors reported, not just the first one.

Hm, that's questionable.  The error is already reported so why bother 
user with duplicates (also hurting performance)?

>>>   	  case IFN_ASAN_CHECK:
>>> -	    ctx->asan_num_accesses++;
>>> +	    if (asan_check_optimize)
>>> +	      remove = maybe_optimize_asan_check_ifn (ctx, stmt);
>>
>> It may be useful to also store base address in check-table:
>>
>> static tree
>> maybe_get_single_definition (tree t)
>> {
>>    if (TREE_CODE (t) == SSA_NAME)
>>      {
>>        gimple g = SSA_NAME_DEF_STMT (t);
>>        if (gimple_assign_single_p (g))
>>          return gimple_assign_rhs1 (g);
>>      }
>>    return NULL_TREE;
>> }
>
> Why?  forwprop etc. should have propagated it into the ASAN_CHECK if
> it is is_gimple_val.  Or do you have specific examples which you have in
> mind?

Yes, non-gimple cases (struct field addresses, etc.) are not propagated 
but still seem ok to optimize.  So for each SSA name we'll store both it 
and it's base definition in table.  This way different SSA with same 
base can optimize each other out.

-Y

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize UBSAN_NULL checks, add sanopt.c
  2014-11-12 10:11                           ` Jakub Jelinek
@ 2014-11-12 11:54                             ` Maxim Ostapenko
  2014-11-12 22:53                               ` [PATCH] Propagate nonfreeing_call_p using ipa-pure-const Jakub Jelinek
  0 siblings, 1 reply; 49+ messages in thread
From: Maxim Ostapenko @ 2014-11-12 11:54 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Yury Gribov, Marek Polacek, GCC Patches, Konstantin Serebryany,
	Maxim Ostapenko

[-- Attachment #1: Type: text/plain, Size: 1286 bytes --]


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

[-- Attachment #2: ipa-nonfreeing.diff --]
[-- Type: text/x-patch, Size: 8074 bytes --]

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

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH] Optimize ASAN_CHECK checks
  2014-11-12 11:12                 ` Yury Gribov
@ 2014-11-12 22:41                   ` Jakub Jelinek
  2014-11-14 11:31                     ` Dodji Seketeli
  0 siblings, 1 reply; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-12 22:41 UTC (permalink / raw)
  To: Dodji Seketeli, Yury Gribov; +Cc: Jan Hubicka, Marek Polacek, GCC Patches

On Wed, Nov 12, 2014 at 02:09:59PM +0300, Yury Gribov wrote:
> >For asan you're right, we can have addresses of decls there etc.
> >If you have spare cycles, feel free to take over the patch and adjust it.
> 
> I guess I'd wait when this gets to trunk?

Ok, in that case I've bootstrapped/regtested on x86_64-linux/i686-linux what I have with
the ASAN_CHECK_NON_ZERO_LEN stuff removed from it (all non-INTEGER_CST
lengths ignored).  Dodji, is this ok for trunk?

2014-11-12  Jakub Jelinek  <jakub@redhat.com>
	    Marek Polacek  <polacek@redhat.com>

	* sanopt.c: Include tree-ssa-operands.h.
	(struct sanopt_info): Add has_freeing_call_p,
	has_freeing_call_computed_p, imm_dom_path_with_freeing_call_p,
	imm_dom_path_with_freeing_call_computed_p, freeing_call_events,
	being_visited_p fields.
	(struct sanopt_ctx): Add asan_check_map field.
	(imm_dom_path_with_freeing_call, maybe_optimize_ubsan_null_ifn,
	maybe_optimize_asan_check_ifn): New functions.
	(sanopt_optimize_walker): Use them, optimize even ASAN_CHECK
	internal calls.
	(pass_sanopt::execute): Call sanopt_optimize even for
	-fsanitize=address.
	* gimple.c (nonfreeing_call_p): Return true for non-ECF_LEAF
	internal calls.

--- gcc/sanopt.c.jj	2014-11-12 08:06:51.497182216 +0100
+++ gcc/sanopt.c	2014-11-12 21:04:50.007325020 +0100
@@ -49,6 +49,7 @@ along with GCC; see the file COPYING3.
 #include "langhooks.h"
 #include "ubsan.h"
 #include "params.h"
+#include "tree-ssa-operands.h"
 
 
 /* This is used to carry information about basic blocks.  It is
@@ -56,7 +57,29 @@ along with GCC; see the file COPYING3.
 
 struct sanopt_info
 {
-  /* True if this BB has been visited.  */
+  /* True if this BB might call (directly or indirectly) free/munmap
+     or similar operation.  */
+  bool has_freeing_call_p;
+
+  /* True if HAS_FREEING_CALL_P flag has been computed.  */
+  bool has_freeing_call_computed_p;
+
+  /* True if there is a block with HAS_FREEING_CALL_P flag set
+     on any a path between imm(BB) and BB.  */
+  bool imm_dom_path_with_freeing_call_p;
+
+  /* True if IMM_DOM_PATH_WITH_FREEING_CALL_P has been computed.  */
+  bool imm_dom_path_with_freeing_call_computed_p;
+
+  /* Number of possibly freeing calls encountered in this bb
+     (so far).  */
+  uint64_t freeing_call_events;
+
+  /* True if BB is currently being visited during computation
+     of IMM_DOM_PATH_WITH_FREEING_CALL_P flag.  */
+  bool being_visited_p;
+
+  /* True if this BB has been visited in the dominator walk.  */
   bool visited_p;
 };
 
@@ -69,11 +92,301 @@ struct sanopt_ctx
      a vector of UBSAN_NULL call statements that check this pointer.  */
   hash_map<tree, auto_vec<gimple> > null_check_map;
 
+  /* This map maps a pointer (the second argument of ASAN_CHECK) to
+     a vector of ASAN_CHECK call statements that check the access.  */
+  hash_map<tree, auto_vec<gimple> > asan_check_map;
+
   /* Number of IFN_ASAN_CHECK statements.  */
   int asan_num_accesses;
 };
 
 
+/* Return true if there might be any call to free/munmap operation
+   on any path in between DOM (which should be imm(BB)) and BB.  */
+
+static bool
+imm_dom_path_with_freeing_call (basic_block bb, basic_block dom)
+{
+  sanopt_info *info = (sanopt_info *) bb->aux;
+  edge e;
+  edge_iterator ei;
+
+  if (info->imm_dom_path_with_freeing_call_computed_p)
+    return info->imm_dom_path_with_freeing_call_p;
+
+  info->being_visited_p = true;
+
+  FOR_EACH_EDGE (e, ei, bb->preds)
+    {
+      sanopt_info *pred_info = (sanopt_info *) e->src->aux;
+
+      if (e->src == dom)
+	continue;
+
+      if ((pred_info->imm_dom_path_with_freeing_call_computed_p
+	  && pred_info->imm_dom_path_with_freeing_call_p)
+	  || (pred_info->has_freeing_call_computed_p
+	      && pred_info->has_freeing_call_p))
+	{
+	  info->imm_dom_path_with_freeing_call_computed_p = true;
+	  info->imm_dom_path_with_freeing_call_p = true;
+	  info->being_visited_p = false;
+	  return true;
+	}
+    }
+
+  FOR_EACH_EDGE (e, ei, bb->preds)
+    {
+      sanopt_info *pred_info = (sanopt_info *) e->src->aux;
+
+      if (e->src == dom)
+	continue;
+
+      if (pred_info->has_freeing_call_computed_p)
+	continue;
+
+      gimple_stmt_iterator gsi;
+      for (gsi = gsi_start_bb (e->src); !gsi_end_p (gsi); gsi_next (&gsi))
+	{
+	  gimple stmt = gsi_stmt (gsi);
+
+	  if (is_gimple_call (stmt) && !nonfreeing_call_p (stmt))
+	    {
+	      pred_info->has_freeing_call_p = true;
+	      break;
+	    }
+	}
+
+      pred_info->has_freeing_call_computed_p = true;
+      if (pred_info->has_freeing_call_p)
+	{
+	  info->imm_dom_path_with_freeing_call_computed_p = true;
+	  info->imm_dom_path_with_freeing_call_p = true;
+	  info->being_visited_p = false;
+	  return true;
+	}
+    }
+
+  FOR_EACH_EDGE (e, ei, bb->preds)
+    {
+      if (e->src == dom)
+	continue;
+
+      basic_block src;
+      for (src = e->src; src != dom; )
+	{
+	  sanopt_info *pred_info = (sanopt_info *) src->aux;
+	  if (pred_info->being_visited_p)
+	    break;
+	  basic_block imm = get_immediate_dominator (CDI_DOMINATORS, src);
+	  if (imm_dom_path_with_freeing_call (src, imm))
+	    {
+	      info->imm_dom_path_with_freeing_call_computed_p = true;
+	      info->imm_dom_path_with_freeing_call_p = true;
+	      info->being_visited_p = false;
+	      return true;
+	    }
+	  src = imm;
+	}
+    }
+
+  info->imm_dom_path_with_freeing_call_computed_p = true;
+  info->imm_dom_path_with_freeing_call_p = false;
+  info->being_visited_p = false;
+  return false;
+}
+
+/* Optimize away redundant UBSAN_NULL calls.  */
+
+static bool
+maybe_optimize_ubsan_null_ifn (struct sanopt_ctx *ctx, gimple stmt)
+{
+  gcc_assert (gimple_call_num_args (stmt) == 3);
+  tree ptr = gimple_call_arg (stmt, 0);
+  tree cur_align = gimple_call_arg (stmt, 2);
+  gcc_assert (TREE_CODE (cur_align) == INTEGER_CST);
+  bool remove = false;
+
+  auto_vec<gimple> &v = ctx->null_check_map.get_or_insert (ptr);
+  if (v.is_empty ())
+    {
+      /* For this PTR we don't have any UBSAN_NULL stmts recorded, so there's
+	 nothing to optimize yet.  */
+      v.safe_push (stmt);
+      return false;
+    }
+
+  /* We already have recorded a UBSAN_NULL check for this pointer. Perhaps we
+     can drop this one.  But only if this check doesn't specify stricter
+     alignment.  */
+  while (!v.is_empty ())
+    {
+      gimple g = v.last ();
+      /* Remove statements for BBs that have been already processed.  */
+      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
+      if (si->visited_p)
+	{
+	  v.pop ();
+	  continue;
+	}
+
+      /* At this point we shouldn't have any statements that aren't dominating
+	 the current BB.  */
+      tree align = gimple_call_arg (g, 2);
+      int kind = tree_to_shwi (gimple_call_arg (g, 1));
+      /* If this is a NULL pointer check where we had segv anyway, we can
+	 remove it.  */
+      if (integer_zerop (align)
+	  && (kind == UBSAN_LOAD_OF
+	      || kind == UBSAN_STORE_OF
+	      || kind == UBSAN_MEMBER_ACCESS))
+	remove = true;
+      /* Otherwise remove the check in non-recovering mode, or if the
+	 stmts have same location.  */
+      else if (integer_zerop (align))
+	remove = (flag_sanitize_recover & SANITIZE_NULL) == 0
+		 || flag_sanitize_undefined_trap_on_error
+		 || gimple_location (g) == gimple_location (stmt);
+      else if (tree_int_cst_le (cur_align, align))
+	remove = (flag_sanitize_recover & SANITIZE_ALIGNMENT) == 0
+		 || flag_sanitize_undefined_trap_on_error
+		 || gimple_location (g) == gimple_location (stmt);
+      if (!remove && gimple_bb (g) == gimple_bb (stmt)
+	  && tree_int_cst_compare (cur_align, align) == 0)
+	v.pop ();
+      break;
+    }
+
+  if (!remove)
+    v.safe_push (stmt);
+  return remove;
+}
+
+/* Optimize away redundant ASAN_CHECK calls.  */
+
+static bool
+maybe_optimize_asan_check_ifn (struct sanopt_ctx *ctx, gimple stmt)
+{
+  gcc_assert (gimple_call_num_args (stmt) == 4);
+  tree ptr = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
+  basic_block bb = gimple_bb (stmt);
+  sanopt_info *info = (sanopt_info *) bb->aux;
+
+  if (TREE_CODE (len) != INTEGER_CST)
+    return false;
+  if (integer_zerop (len))
+    return false;
+
+  gimple_set_uid (stmt, info->freeing_call_events);
+
+  auto_vec<gimple> &v = ctx->asan_check_map.get_or_insert (ptr);
+  if (v.is_empty ())
+    {
+      /* For this PTR we don't have any ASAN_CHECK stmts recorded, so there's
+	 nothing to optimize yet.  */
+      v.safe_push (stmt);
+      return false;
+    }
+
+  /* We already have recorded a ASAN_CHECK check for this pointer.  Perhaps
+     we can drop this one.  But only if this check doesn't specify larger
+     size.  */
+  while (!v.is_empty ())
+    {
+      gimple g = v.last ();
+      /* Remove statements for BBs that have been already processed.  */
+      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
+      if (si->visited_p)
+	v.pop ();
+      else
+	break;
+    }
+
+  unsigned int i;
+  gimple g;
+  gimple to_pop = NULL;
+  bool remove = false;
+  basic_block last_bb = bb;
+  bool cleanup = false;
+
+  FOR_EACH_VEC_ELT_REVERSE (v, i, g)
+    {
+      basic_block gbb = gimple_bb (g);
+      sanopt_info *si = (sanopt_info *) gbb->aux;
+      if (gimple_uid (g) < si->freeing_call_events)
+	{
+	  /* If there is a potentially freeing call after g in gbb, we should
+	     remove it from the vector, can't use in optimization.  */
+	  cleanup = true;
+	  continue;
+	}
+
+      if (TREE_CODE (len) != INTEGER_CST)
+	{
+	  /* If there is some stmts not followed by freeing call event
+	     for ptr already in the current bb, no need to insert anything.
+	     Non-constant len is treated just as length 1.  */
+	  if (gbb == bb)
+	    return false;
+	  break;
+	}
+
+      tree glen = gimple_call_arg (g, 2);
+      /* If we've checked only smaller length than we want to check now,
+	 we can't remove the current stmt.  If g is in the same basic block,
+	 we want to remove it though, as the current stmt is better.  */
+      if (tree_int_cst_lt (glen, len))
+	{
+	  if (gbb == bb)
+	    {
+	      to_pop = g;
+	      cleanup = true;
+	    }
+	  continue;
+	}
+
+      while (last_bb != gbb)
+	{
+	  /* Paths from last_bb to bb have been checked before.
+	     gbb is necessarily a dominator of last_bb, but not necessarily
+	     immediate dominator.  */
+	  if (((sanopt_info *) last_bb->aux)->freeing_call_events)
+	    break;
+
+	  basic_block imm = get_immediate_dominator (CDI_DOMINATORS, last_bb);
+	  gcc_assert (imm);
+	  if (imm_dom_path_with_freeing_call (last_bb, imm))
+	    break;
+
+	  last_bb = imm;
+	}
+      if (last_bb == gbb)
+	remove = true;
+      break;
+    }
+
+  if (cleanup)
+    {
+      unsigned int j = 0, l = v.length ();
+      for (i = 0; i < l; i++)
+	if (v[i] != to_pop
+	    && (gimple_uid (v[i])
+		== ((sanopt_info *)
+		    gimple_bb (v[i])->aux)->freeing_call_events))
+	  {
+	    if (i != j)
+	      v[j] = v[i];
+	    j++;
+	  }
+      v.truncate (j);
+    }
+
+  if (!remove)
+    v.safe_push (stmt);
+  return remove;
+}
+
 /* Try to optimize away redundant UBSAN_NULL checks.
    
    We walk blocks in the CFG via a depth first search of the dominator
@@ -89,111 +402,77 @@ sanopt_optimize_walker (basic_block bb,
 {
   basic_block son;
   gimple_stmt_iterator gsi;
+  sanopt_info *info = (sanopt_info *) bb->aux;
+  bool asan_check_optimize
+    = (flag_sanitize & SANITIZE_ADDRESS)
+      && ((flag_sanitize & flag_sanitize_recover
+	   & SANITIZE_KERNEL_ADDRESS) == 0);
 
   for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
     {
       gimple stmt = gsi_stmt (gsi);
       bool remove = false;
 
-      if (is_gimple_call (stmt)
-	  && gimple_call_internal_p (stmt))
+      if (!is_gimple_call (stmt))
+	{
+	  /* Handle asm volatile or asm with "memory" clobber
+	     the same as potentionally freeing call.  */
+	  if (gimple_code (stmt) == GIMPLE_ASM
+	      && asan_check_optimize
+	      && (gimple_asm_clobbers_memory_p (stmt)
+		  || gimple_asm_volatile_p (stmt)))
+	    info->freeing_call_events++;
+	  gsi_next (&gsi);
+	  continue;
+	}
+
+      if (asan_check_optimize && !nonfreeing_call_p (stmt))
+	info->freeing_call_events++;
+
+      if (gimple_call_internal_p (stmt))
 	switch (gimple_call_internal_fn (stmt))
 	  {
 	  case IFN_UBSAN_NULL:
-	    {
-	      gcc_assert (gimple_call_num_args (stmt) == 3);
-	      tree ptr = gimple_call_arg (stmt, 0);
-	      tree cur_align = gimple_call_arg (stmt, 2);
-	      gcc_assert (TREE_CODE (cur_align) == INTEGER_CST);
-
-	      auto_vec<gimple> &v = ctx->null_check_map.get_or_insert (ptr);
-	      if (v.is_empty ())
-		/* For this PTR we don't have any UBSAN_NULL stmts
-		   recorded, so there's nothing to optimize yet.  */
-		v.safe_push (stmt);
-	      else
-		{
-		  /* We already have recorded a UBSAN_NULL check
-		     for this pointer.  Perhaps we can drop this one.
-		     But only if this check doesn't specify stricter
-		     alignment.  */
-		  while (!v.is_empty ())
-		    {
-		      gimple g = v.last ();
-		      /* Remove statements for BBs that have been
-			 already processed.  */
-		      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
-		      if (si->visited_p)
-			v.pop ();
-		      else
-			{
-			  /* At this point we shouldn't have any statements
-			     that aren't dominating the current BB.  */
-			  tree align = gimple_call_arg (g, 2);
-			  int kind = tree_to_shwi (gimple_call_arg (g, 1));
-			  /* If this is a NULL pointer check where we had segv
-			     anyway, we can remove it.  */
-			  if (integer_zerop (align)
-			      && (kind == UBSAN_LOAD_OF
-				  || kind == UBSAN_STORE_OF
-				  || kind == UBSAN_MEMBER_ACCESS))
-			    remove = true;
-			  /* Otherwise remove the check in non-recovering
-			     mode, or if the stmts have same location.  */
-			  else if (integer_zerop (align))
-			    remove = !(flag_sanitize_recover & SANITIZE_NULL)
-				     || flag_sanitize_undefined_trap_on_error
-				     || gimple_location (g)
-					== gimple_location (stmt);
-			  else if (tree_int_cst_le (cur_align, align))
-			    remove = !(flag_sanitize_recover
-				       & SANITIZE_ALIGNMENT)
-				     || flag_sanitize_undefined_trap_on_error
-				     || gimple_location (g)
-					== gimple_location (stmt);
-			  if (!remove && gimple_bb (g) == gimple_bb (stmt)
-			      && tree_int_cst_compare (cur_align, align) == 0)
-			    v.pop ();
-			  break;
-			}
-		    }
-
-		  if (remove)
-		    {
-		      /* Drop this check.  */
-		      if (dump_file && (dump_flags & TDF_DETAILS))
-			{
-			  fprintf (dump_file, "Optimizing out\n  ");
-			  print_gimple_stmt (dump_file, stmt, 0,
-					     dump_flags);
-			  fprintf (dump_file, "\n");
-			}
-		      gsi_remove (&gsi, true);
-		    }
-		  else
-		    v.safe_push (stmt);
-		  }
-	    }
+	    remove = maybe_optimize_ubsan_null_ifn (ctx, stmt);
+	    break;
 	  case IFN_ASAN_CHECK:
-	    ctx->asan_num_accesses++;
+	    if (asan_check_optimize)
+	      remove = maybe_optimize_asan_check_ifn (ctx, stmt);
+	    if (!remove)
+	      ctx->asan_num_accesses++;
 	    break;
 	  default:
 	    break;
 	  }
 
-      /* If we were able to remove the current statement, gsi_remove
-	 already pointed us to the next statement.  */
-      if (!remove)
+      if (remove)
+	{
+	  /* Drop this check.  */
+	  if (dump_file && (dump_flags & TDF_DETAILS))
+	    {
+	      fprintf (dump_file, "Optimizing out\n  ");
+	      print_gimple_stmt (dump_file, stmt, 0, dump_flags);
+	      fprintf (dump_file, "\n");
+	    }
+	  unlink_stmt_vdef (stmt);
+	  gsi_remove (&gsi, true);
+	}
+      else
 	gsi_next (&gsi);
     }
 
+  if (asan_check_optimize)
+    {
+      info->has_freeing_call_p = info->freeing_call_events != 0;
+      info->has_freeing_call_computed_p = true;
+    }
+
   for (son = first_dom_son (CDI_DOMINATORS, bb);
        son;
        son = next_dom_son (CDI_DOMINATORS, son))
     sanopt_optimize_walker (son, ctx);
 
   /* We're leaving this BB, so mark it to that effect.  */
-  sanopt_info *info = (sanopt_info *) bb->aux;
   info->visited_p = true;
 }
 
@@ -259,7 +538,8 @@ pass_sanopt::execute (function *fun)
 
   /* Try to remove redundant checks.  */
   if (optimize
-      && (flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT)))
+      && (flag_sanitize
+	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_ADDRESS)))
     asan_num_accesses = sanopt_optimize (fun);
   else if (flag_sanitize & SANITIZE_ADDRESS)
     {
--- gcc/gimple.c.jj	2014-11-12 08:06:51.000000000 +0100
+++ gcc/gimple.c	2014-11-12 21:01:52.532940986 +0100
@@ -2538,6 +2538,9 @@ nonfreeing_call_p (gimple call)
 	default:
 	  return true;
       }
+  else if (gimple_call_internal_p (call)
+	   && gimple_call_flags (call) & ECF_LEAF)
+    return true;
 
   return false;
 }


	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH] Propagate nonfreeing_call_p using ipa-pure-const
  2014-11-12 11:54                             ` Maxim Ostapenko
@ 2014-11-12 22:53                               ` Jakub Jelinek
  2014-11-12 23:11                                 ` Jan Hubicka
  0 siblings, 1 reply; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-12 22:53 UTC (permalink / raw)
  To: Jan Hubicka, Maxim Ostapenko
  Cc: Yury Gribov, Marek Polacek, GCC Patches, Konstantin Serebryany,
	Maxim Ostapenko

On Wed, Nov 12, 2014 at 02:49:30PM +0400, Maxim Ostapenko wrote:
> 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.

Here it is rewritten using ipa-pure-const which is where Richard/Honza
suggested it should be done in.

I wonder if the nonfreeing_call_p function shouldn't be moved elsewhere
though (suggestion where), so that gimple.c doesn't need the cgraph
includes.

In any case, bootstrapped/regtested on x86_64-linux and i686-linux.

2014-11-12  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_can_free): New function.
	(pass_ipa_pure_const::execute): Call it.
	* 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.
	* 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.

--- gcc/ipa-pure-const.c.jj	2014-11-12 18:32:56.351139726 +0100
+++ gcc/ipa-pure-const.c	2014-11-12 21:11:08.574354600 +0100
@@ -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 ca
       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 ca
 	    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,
 	    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))
 	{
@@ -761,6 +772,7 @@ check_stmt (gimple_stmt_iterator *gsip,
 	  /* Abandon all hope, ye who enter here. */
 	  local->pure_const_state = IPA_NEITHER;
           local->looping = true;
+	  local->can_free = true;
 	}
       return;
     default:
@@ -785,6 +797,7 @@ analyze_function (struct cgraph_node *fn
   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
 	   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");
 		}
 	    }
 
@@ -1510,6 +1532,82 @@ propagate_nothrow (void)
   free (order);
 }
 
+/* Produce transitive closure over the callgraph and compute can_free
+   attributes.  */
+
+static void
+propagate_can_free (void)
+{
+  struct cgraph_node *node;
+  struct cgraph_node *w;
+  struct cgraph_node **order
+    = XCNEWVEC (struct cgraph_node *, symtab->cgraph_count);
+  int order_pos;
+  int i;
+  struct ipa_dfs_info *w_info;
+
+  order_pos = ipa_reduced_postorder (order, true, false, NULL);
+  if (dump_file)
+    {
+      cgraph_node::dump_cgraph (dump_file);
+      ipa_print_order (dump_file, "reduced", order, order_pos);
+    }
+
+  /* Propagate the local information through the call graph to produce
+     the global information.  All the nodes within a cycle will have
+     the same info so we collapse cycles first.  Then we can do the
+     propagation in one pass from the leaves to the roots.  */
+  for (i = 0; i < order_pos; i++ )
+    {
+      bool can_free = false;
+      node = order[i];
+
+      if (node->alias)
+	continue;
+
+      /* Find the worst state for any node in the cycle.  */
+      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 can_free which is shared by
+	 all nodes in the region.  */
+      w = node;
+      while (w)
+	{
+	  funct_state w_l = get_function_state (w);
+	  w_l->can_free = can_free;
+	  w->nonfreeing_fn = !can_free;
+	  w_info = (struct ipa_dfs_info *) w->aux;
+	  w = w_info->next_cycle;
+	}
+    }
+
+  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.  */
@@ -1528,6 +1626,7 @@ execute (function *)
      later analysis.  */
   propagate_nothrow ();
   propagate_pure_const ();
+  propagate_can_free ();
 
   /* Cleanup. */
   FOR_EACH_FUNCTION (node)
--- gcc/cgraph.h.jj	2014-11-12 18:09:27.784014976 +0100
+++ gcc/cgraph.h	2014-11-12 21:05:53.851895859 +0100
@@ -1260,6 +1260,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
--- gcc/gimple.c.jj	2014-11-12 21:01:52.532940986 +0100
+++ gcc/gimple.c	2014-11-12 21:05:53.849895526 +0100
@@ -58,6 +58,9 @@ along with GCC; see the file COPYING3.
 #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
@@ -2542,7 +2545,13 @@ nonfreeing_call_p (gimple call)
 	   && gimple_call_flags (call) & ECF_LEAF)
     return true;
 
-  return false;
+  tree fndecl = gimple_call_fndecl (call);
+  if (!fndecl)
+    return false;
+  struct cgraph_node *n = cgraph_node::get (fndecl);
+  if (!n || n->get_availability () <= AVAIL_INTERPOSABLE)
+    return false;
+  return n->nonfreeing_fn;
 }
 
 /* Callback for walk_stmt_load_store_ops.
--- gcc/cgraph.c.jj	2014-11-11 00:05:42.000000000 +0100
+++ gcc/cgraph.c	2014-11-12 21:05:53.848895354 +0100
@@ -1986,6 +1986,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))
--- gcc/lto-cgraph.c.jj	2014-11-11 00:06:11.000000000 +0100
+++ gcc/lto-cgraph.c	2014-11-12 21:05:53.846895004 +0100
@@ -549,6 +549,7 @@ lto_output_node (struct lto_simple_outpu
   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);
@@ -1097,6 +1098,7 @@ input_overwrite_node (struct lto_file_de
   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);


	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Propagate nonfreeing_call_p using ipa-pure-const
  2014-11-12 22:53                               ` [PATCH] Propagate nonfreeing_call_p using ipa-pure-const Jakub Jelinek
@ 2014-11-12 23:11                                 ` Jan Hubicka
  2014-11-13  7:45                                   ` Jakub Jelinek
  2014-11-13  8:44                                   ` [PATCH] Propagate nonfreeing_call_p using ipa-pure-const (take 2) Jakub Jelinek
  0 siblings, 2 replies; 49+ messages in thread
From: Jan Hubicka @ 2014-11-12 23:11 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Jan Hubicka, Maxim Ostapenko, Yury Gribov, Marek Polacek,
	GCC Patches, Konstantin Serebryany, Maxim Ostapenko

> On Wed, Nov 12, 2014 at 02:49:30PM +0400, Maxim Ostapenko wrote:
> > 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.
> 
> Here it is rewritten using ipa-pure-const which is where Richard/Honza
> suggested it should be done in.
> 
> I wonder if the nonfreeing_call_p function shouldn't be moved elsewhere
> though (suggestion where), so that gimple.c doesn't need the cgraph
> includes.
> 
> In any case, bootstrapped/regtested on x86_64-linux and i686-linux.
> 
> 2014-11-12  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_can_free): New function.
> 	(pass_ipa_pure_const::execute): Call it.
> 	* 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.
> 	* 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.
> 
> --- gcc/ipa-pure-const.c.jj	2014-11-12 18:32:56.351139726 +0100
> +++ gcc/ipa-pure-const.c	2014-11-12 21:11:08.574354600 +0100
> @@ -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 ca
>        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 ca
>  	    break;
>  	  }
>      }
> +  else if (gimple_call_internal_p (call) && !nonfreeing_call_p (call))
> +    local->can_free = true;

Actually I think you want to do this for can_throw, too.
We probably do not have throwing internal calls, but it is better to be safe.
> +/* Produce transitive closure over the callgraph and compute can_free
> +   attributes.  */
> +
> +static void
> +propagate_can_free (void)
> +{
> +  struct cgraph_node *node;
> +  struct cgraph_node *w;
> +  struct cgraph_node **order
> +    = XCNEWVEC (struct cgraph_node *, symtab->cgraph_count);
> +  int order_pos;
> +  int i;
> +  struct ipa_dfs_info *w_info;
> +
> +  order_pos = ipa_reduced_postorder (order, true, false, NULL);
> +  if (dump_file)
> +    {
> +      cgraph_node::dump_cgraph (dump_file);
> +      ipa_print_order (dump_file, "reduced", order, order_pos);
> +    }

The propagation seems fine, but I wonder if we won't get better memory locality doing this
during the propagation of pure/const?

nothrow flag goes in separate loop because knowledge of nothrow helps pure/const to work better.
Also one can ignore call edges that are !can_thros_externally to get fewer cycles, but apparently
this got never implemented.
>  	   && gimple_call_flags (call) & ECF_LEAF)
>      return true;
>  
> -  return false;
> +  tree fndecl = gimple_call_fndecl (call);
> +  if (!fndecl)
> +    return false;
> +  struct cgraph_node *n = cgraph_node::get (fndecl);

You want to walk aliases,

  cgraph_node::get (fndecl)->function_symbol (&availability)

> +  if (!n || n->get_availability () <= AVAIL_INTERPOSABLE)

and use availability here.

OK with this change.

Honza

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Propagate nonfreeing_call_p using ipa-pure-const
  2014-11-12 23:11                                 ` Jan Hubicka
@ 2014-11-13  7:45                                   ` Jakub Jelinek
  2014-11-13  8:44                                   ` [PATCH] Propagate nonfreeing_call_p using ipa-pure-const (take 2) Jakub Jelinek
  1 sibling, 0 replies; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-13  7:45 UTC (permalink / raw)
  To: Jan Hubicka
  Cc: Maxim Ostapenko, Yury Gribov, Marek Polacek, GCC Patches,
	Konstantin Serebryany, Maxim Ostapenko

On Thu, Nov 13, 2014 at 12:11:08AM +0100, Jan Hubicka wrote:
> > +  else if (gimple_call_internal_p (call) && !nonfreeing_call_p (call))
> > +    local->can_free = true;
> 
> Actually I think you want to do this for can_throw, too.
> We probably do not have throwing internal calls, but it is better to be safe.

The only border case internal function is ABNORMAL_DISPATCHER, the only
internal function right now that is not ECF_LEAF.  It doesn't throw, but is
used for sjlj EH.
That said, I don't see any special handling of if (callee_t) for can_throw
(for internal functions it will be always false), only for
looping/pure_const_state, but that seems to be only about doing something
for selected functions (builtin or specially named - setjmp*).
Or do you mean pretend that callee_t is non-NULL for internal functions?

> > +/* Produce transitive closure over the callgraph and compute can_free
> > +   attributes.  */
> > +
> > +static void
> > +propagate_can_free (void)
> > +{
> > +  struct cgraph_node *node;
> > +  struct cgraph_node *w;
> > +  struct cgraph_node **order
> > +    = XCNEWVEC (struct cgraph_node *, symtab->cgraph_count);
> > +  int order_pos;
> > +  int i;
> > +  struct ipa_dfs_info *w_info;
> > +
> > +  order_pos = ipa_reduced_postorder (order, true, false, NULL);
> > +  if (dump_file)
> > +    {
> > +      cgraph_node::dump_cgraph (dump_file);
> > +      ipa_print_order (dump_file, "reduced", order, order_pos);
> > +    }
> 
> The propagation seems fine, but I wonder if we won't get better memory locality doing this
> during the propagation of pure/const?

Ok, will try to put it there.

	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH] Propagate nonfreeing_call_p using ipa-pure-const (take 2)
  2014-11-12 23:11                                 ` Jan Hubicka
  2014-11-13  7:45                                   ` Jakub Jelinek
@ 2014-11-13  8:44                                   ` Jakub Jelinek
  2014-11-13 11:08                                     ` Richard Biener
  2014-11-13 12:05                                     ` Jakub Jelinek
  1 sibling, 2 replies; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-13  8:44 UTC (permalink / raw)
  To: Jan Hubicka, Richard Biener
  Cc: Maxim Ostapenko, Yury Gribov, Marek Polacek, GCC Patches,
	Konstantin Serebryany, Maxim Ostapenko

Hi!

On Thu, Nov 13, 2014 at 12:11:08AM +0100, Jan Hubicka wrote:

> Actually I think you want to do this for can_throw, too.
> We probably do not have throwing internal calls, but it is better to be safe.

I'll leave that change to you ;), as I said in my last mail, it isn't
immediately clear to me what exactly you want to do with internal calls
for can_throw (if anything) and for pure_const/looping.
E.g. for pure_const, internal calls can be memory accesses (e.g.
MASK_LOAD/MASK_STORE/LOAD_LANES/STORE_LANES, supposedly even ASAN_CHECK
can be considered a memory access).  Though, the first 4 should ATM
appear only post-IPA, and ASAN_CHECK should just accompany some normal
memory access.

> The propagation seems fine, but I wonder if we won't get better memory locality doing this
> during the propagation of pure/const?

Done below, though I chose to use a separate w = node; ... w = w_info->next_cycle;
loop to compute can_free, because trying to do it in the same loop as
pure_const which can stop early etc. would be too ugly.  The update of
can_free and setting of nonfreeing_fn flag is done in the same loop as
storing of pure_const/looping results.

> You want to walk aliases,
> 
>   cgraph_node::get (fndecl)->function_symbol (&availability)
> 
> > +  if (!n || n->get_availability () <= AVAIL_INTERPOSABLE)
> 
> and use availability here.

Done below.

What about the:
> > I wonder if the nonfreeing_call_p function shouldn't be moved elsewhere
> > though (suggestion where), so that gimple.c doesn't need the cgraph
> > includes.
question though (maybe it is more on Richard)?

2014-11-13  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.
	* 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.

--- gcc/cgraph.c.jj	2014-11-13 00:07:34.271125586 +0100
+++ gcc/cgraph.c	2014-11-13 09:14:41.217090244 +0100
@@ -1986,6 +1986,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))
--- gcc/ipa-pure-const.c.jj	2014-11-13 00:08:37.566011221 +0100
+++ gcc/ipa-pure-const.c	2014-11-13 09:25:02.933637928 +0100
@@ -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 ca
       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 ca
 	    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,
 	    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,
 	    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
   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
 	   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)
 	    {
--- gcc/cgraph.h.jj	2014-11-13 00:07:34.278125463 +0100
+++ gcc/cgraph.h	2014-11-13 09:14:41.216090285 +0100
@@ -1260,6 +1260,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
--- gcc/gimple.c.jj	2014-11-13 09:14:28.853595937 +0100
+++ gcc/gimple.c	2014-11-13 09:16:08.111606042 +0100
@@ -58,6 +58,9 @@ along with GCC; see the file COPYING3.
 #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
@@ -2542,7 +2545,17 @@ nonfreeing_call_p (gimple call)
 	   && gimple_call_flags (call) & ECF_LEAF)
     return true;
 
-  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.
--- gcc/lto-cgraph.c.jj	2014-11-13 00:07:34.306124970 +0100
+++ gcc/lto-cgraph.c	2014-11-13 09:14:41.227089835 +0100
@@ -549,6 +549,7 @@ lto_output_node (struct lto_simple_outpu
   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);
@@ -1097,6 +1098,7 @@ input_overwrite_node (struct lto_file_de
   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);


	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Propagate nonfreeing_call_p using ipa-pure-const (take 2)
  2014-11-13  8:44                                   ` [PATCH] Propagate nonfreeing_call_p using ipa-pure-const (take 2) Jakub Jelinek
@ 2014-11-13 11:08                                     ` Richard Biener
  2014-11-13 12:05                                     ` Jakub Jelinek
  1 sibling, 0 replies; 49+ messages in thread
From: Richard Biener @ 2014-11-13 11:08 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Jan Hubicka, Maxim Ostapenko, Yury Gribov, Marek Polacek,
	GCC Patches, Konstantin Serebryany, Maxim Ostapenko

On Thu, Nov 13, 2014 at 9:39 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> Hi!
>
> On Thu, Nov 13, 2014 at 12:11:08AM +0100, Jan Hubicka wrote:
>
>> Actually I think you want to do this for can_throw, too.
>> We probably do not have throwing internal calls, but it is better to be safe.
>
> I'll leave that change to you ;), as I said in my last mail, it isn't
> immediately clear to me what exactly you want to do with internal calls
> for can_throw (if anything) and for pure_const/looping.
> E.g. for pure_const, internal calls can be memory accesses (e.g.
> MASK_LOAD/MASK_STORE/LOAD_LANES/STORE_LANES, supposedly even ASAN_CHECK
> can be considered a memory access).  Though, the first 4 should ATM
> appear only post-IPA, and ASAN_CHECK should just accompany some normal
> memory access.
>
>> The propagation seems fine, but I wonder if we won't get better memory locality doing this
>> during the propagation of pure/const?
>
> Done below, though I chose to use a separate w = node; ... w = w_info->next_cycle;
> loop to compute can_free, because trying to do it in the same loop as
> pure_const which can stop early etc. would be too ugly.  The update of
> can_free and setting of nonfreeing_fn flag is done in the same loop as
> storing of pure_const/looping results.
>
>> You want to walk aliases,
>>
>>   cgraph_node::get (fndecl)->function_symbol (&availability)
>>
>> > +  if (!n || n->get_availability () <= AVAIL_INTERPOSABLE)
>>
>> and use availability here.
>
> Done below.
>
> What about the:
>> > I wonder if the nonfreeing_call_p function shouldn't be moved elsewhere
>> > though (suggestion where), so that gimple.c doesn't need the cgraph
>> > includes.
> question though (maybe it is more on Richard)?

Well - you could place it in cgraph.[ch].

Richard.

> 2014-11-13  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.
>         * 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.
>
> --- gcc/cgraph.c.jj     2014-11-13 00:07:34.271125586 +0100
> +++ gcc/cgraph.c        2014-11-13 09:14:41.217090244 +0100
> @@ -1986,6 +1986,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))
> --- gcc/ipa-pure-const.c.jj     2014-11-13 00:08:37.566011221 +0100
> +++ gcc/ipa-pure-const.c        2014-11-13 09:25:02.933637928 +0100
> @@ -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 ca
>        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 ca
>             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,
>             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,
>             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
>    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
>            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)
>             {
> --- gcc/cgraph.h.jj     2014-11-13 00:07:34.278125463 +0100
> +++ gcc/cgraph.h        2014-11-13 09:14:41.216090285 +0100
> @@ -1260,6 +1260,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
> --- gcc/gimple.c.jj     2014-11-13 09:14:28.853595937 +0100
> +++ gcc/gimple.c        2014-11-13 09:16:08.111606042 +0100
> @@ -58,6 +58,9 @@ along with GCC; see the file COPYING3.
>  #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
> @@ -2542,7 +2545,17 @@ nonfreeing_call_p (gimple call)
>            && gimple_call_flags (call) & ECF_LEAF)
>      return true;
>
> -  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.
> --- gcc/lto-cgraph.c.jj 2014-11-13 00:07:34.306124970 +0100
> +++ gcc/lto-cgraph.c    2014-11-13 09:14:41.227089835 +0100
> @@ -549,6 +549,7 @@ lto_output_node (struct lto_simple_outpu
>    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);
> @@ -1097,6 +1098,7 @@ input_overwrite_node (struct lto_file_de
>    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);
>
>
>         Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Propagate nonfreeing_call_p using ipa-pure-const (take 2)
  2014-11-13  8:44                                   ` [PATCH] Propagate nonfreeing_call_p using ipa-pure-const (take 2) Jakub Jelinek
  2014-11-13 11:08                                     ` Richard Biener
@ 2014-11-13 12:05                                     ` Jakub Jelinek
  2014-11-14 17:44                                       ` Jakub Jelinek
  1 sibling, 1 reply; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-13 12:05 UTC (permalink / raw)
  To: Jan Hubicka, Richard Biener
  Cc: Maxim Ostapenko, Yury Gribov, Marek Polacek, GCC Patches,
	Konstantin Serebryany, Maxim Ostapenko

On Thu, Nov 13, 2014 at 09:39:42AM +0100, Jakub Jelinek wrote:
> What about the:
> > > I wonder if the nonfreeing_call_p function shouldn't be moved elsewhere
> > > though (suggestion where), so that gimple.c doesn't need the cgraph
> > > includes.
> question though (maybe it is more on Richard)?

Tried richi's suggested cgraph.[ch], but that meant all users of
nonfreeing_call_p had to start including all of ipa-ref.h, lto-streamer.h
and cgraph.h, so it is probably better to keep it in gimple.[ch].

	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize ASAN_CHECK checks
  2014-11-12 22:41                   ` [PATCH] " Jakub Jelinek
@ 2014-11-14 11:31                     ` Dodji Seketeli
  2014-11-14 12:02                       ` Jakub Jelinek
  0 siblings, 1 reply; 49+ messages in thread
From: Dodji Seketeli @ 2014-11-14 11:31 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Yury Gribov, Jan Hubicka, Marek Polacek, GCC Patches


Jakub Jelinek <jakub@redhat.com> writes:

> On Wed, Nov 12, 2014 at 02:09:59PM +0300, Yury Gribov wrote:
> > >For asan you're right, we can have addresses of decls there etc.
> > >If you have spare cycles, feel free to take over the patch and adjust it.
> > 
> > I guess I'd wait when this gets to trunk?
> 
> Ok, in that case I've bootstrapped/regtested on x86_64-linux/i686-linux what I have with
> the ASAN_CHECK_NON_ZERO_LEN stuff removed from it (all non-INTEGER_CST
> lengths ignored).  Dodji, is this ok for trunk?

[...]

> +++ gcc/sanopt.c	2014-11-12 21:04:50.007325020 +0100
> 
>  /* This is used to carry information about basic blocks.  It is
> @@ -56,7 +57,29 @@ along with GCC; see the file COPYING3.
>  
>  struct sanopt_info
>  {

[...]

> +  /* True if there is a block with HAS_FREEING_CALL_P flag set
> +     on any a path between imm(BB) and BB.  */

s/a//.

Also, I'd rather say "on any path between an immediate dominator of
BB, denoted imm(BB), and BB".  That way, subsequent uses of imm(BB)
makes sense more for the new comer.  This is only a suggestion.  If
you feel that formulation is obvious enough, then please ignore my
comment.

> +  bool imm_dom_path_with_freeing_call_p;

[...]

 };

[...]
 
> +/* Return true if there might be any call to free/munmap operation
> +   on any path in between DOM (which should be imm(BB)) and BB.  */
> +
> +static bool
> +imm_dom_path_with_freeing_call (basic_block bb, basic_block dom)
> +{

To ease maintainability, maybe we could assert that:

   gcc_assert (dom == get_immediate_dominator(CDI_DOMINATORS, bb));

?

And thus remove the assert that is at the caller site of this
function, later in maybe_optimize_asan_check_ifn:

> +	  basic_block imm = get_immediate_dominator (CDI_DOMINATORS, last_bb);
> +	  gcc_assert (imm);
> +	  if (imm_dom_path_with_freeing_call (last_bb, imm))
> +	    break;


Also, when the 'dom' basic block is NULL, couldn't we just return
immediately?

[...]

> +}


[...]

> +/* Optimize away redundant ASAN_CHECK calls.  */
> +
> +static bool
> +maybe_optimize_asan_check_ifn (struct sanopt_ctx *ctx, gimple stmt)
> +{
> +  gcc_assert (gimple_call_num_args (stmt) == 4);
> +  tree ptr = gimple_call_arg (stmt, 1);
> +  tree len = gimple_call_arg (stmt, 2);
> +  basic_block bb = gimple_bb (stmt);
> +  sanopt_info *info = (sanopt_info *) bb->aux;
> +
> +  if (TREE_CODE (len) != INTEGER_CST)
> +    return false;
> +  if (integer_zerop (len))
> +    return false;
> +
> +  gimple_set_uid (stmt, info->freeing_call_events);

I am not sure, but I am wondering if we shouldn't save the previous uid
of 'stmt' here before setting it, and then restore it before getting out
of this function.

[...]

> +}
> +
>  /* Try to optimize away redundant UBSAN_NULL checks.
>     
>     We walk blocks in the CFG via a depth first search of the dominator
> @@ -89,111 +402,77 @@ sanopt_optimize_walker (basic_block bb,

I think the comment of this sanopt_optimize_walker function should now
be adapted to say that it optimizes away redundant UBSAN_NULL *and*
ASAN_CHECK internal function calls.

>  {

[...]

>  }

[...]

OK with those changes.

Thanks.

-- 
		Dodji

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize ASAN_CHECK checks
  2014-11-14 11:31                     ` Dodji Seketeli
@ 2014-11-14 12:02                       ` Jakub Jelinek
  2014-11-14 12:16                         ` Dodji Seketeli
  0 siblings, 1 reply; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-14 12:02 UTC (permalink / raw)
  To: Dodji Seketeli; +Cc: Yury Gribov, Jan Hubicka, Marek Polacek, GCC Patches

On Fri, Nov 14, 2014 at 12:25:37PM +0100, Dodji Seketeli wrote:
> > +  /* True if there is a block with HAS_FREEING_CALL_P flag set
> > +     on any a path between imm(BB) and BB.  */
> 
> s/a//.
> 
> Also, I'd rather say "on any path between an immediate dominator of
> BB, denoted imm(BB), and BB".  That way, subsequent uses of imm(BB)
> makes sense more for the new comer.  This is only a suggestion.  If
> you feel that formulation is obvious enough, then please ignore my
> comment.

Ok.

> > +/* Return true if there might be any call to free/munmap operation
> > +   on any path in between DOM (which should be imm(BB)) and BB.  */
> > +
> > +static bool
> > +imm_dom_path_with_freeing_call (basic_block bb, basic_block dom)
> > +{
> 
> To ease maintainability, maybe we could assert that:
> 
>    gcc_assert (dom == get_immediate_dominator(CDI_DOMINATORS, bb));

Well, that would make the dom argument useless, the point of passing it
around was that because we have to call it in the caller already,
there is no point in calling it again the the callee.
So if we as well call it (most people don't use --disable-checking),
we could just drop the argument.

> ?
> 
> And thus remove the assert that is at the caller site of this
> function, later in maybe_optimize_asan_check_ifn:
> 
> > +	  basic_block imm = get_immediate_dominator (CDI_DOMINATORS, last_bb);
> > +	  gcc_assert (imm);

The assert is very much intentional here, want to double-check the algorithm
that we indeed always reach gbb through the imm dominator walk (unless
bailing out early, but if we wouldn't, we'd reach it).

> > +	  if (imm_dom_path_with_freeing_call (last_bb, imm))
> > +	    break;
> 
> 
> Also, when the 'dom' basic block is NULL, couldn't we just return
> immediately?

get_immediate_dominator (CDI_DOMINATORS, ) should return NULL
just for the ENTRY block, it would be a bug to reach that bb.

> > +/* Optimize away redundant ASAN_CHECK calls.  */
> > +
> > +static bool
> > +maybe_optimize_asan_check_ifn (struct sanopt_ctx *ctx, gimple stmt)
> > +{
> > +  gcc_assert (gimple_call_num_args (stmt) == 4);
> > +  tree ptr = gimple_call_arg (stmt, 1);
> > +  tree len = gimple_call_arg (stmt, 2);
> > +  basic_block bb = gimple_bb (stmt);
> > +  sanopt_info *info = (sanopt_info *) bb->aux;
> > +
> > +  if (TREE_CODE (len) != INTEGER_CST)
> > +    return false;
> > +  if (integer_zerop (len))
> > +    return false;
> > +
> > +  gimple_set_uid (stmt, info->freeing_call_events);
> 
> I am not sure, but I am wondering if we shouldn't save the previous uid
> of 'stmt' here before setting it, and then restore it before getting out
> of this function.

No, gimple uids are AFAIK undefined at the start of passes, passes that use
them are supposed to initialize them before use (new statements created
during the pass will get 0 there by default), and don't have to clean them
up anyway at the end of pass.

> > @@ -89,111 +402,77 @@ sanopt_optimize_walker (basic_block bb,
> 
> I think the comment of this sanopt_optimize_walker function should now
> be adapted to say that it optimizes away redundant UBSAN_NULL *and*
> ASAN_CHECK internal function calls.

Ok, will do.

	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize ASAN_CHECK checks
  2014-11-14 12:02                       ` Jakub Jelinek
@ 2014-11-14 12:16                         ` Dodji Seketeli
  2014-11-14 12:18                           ` Jakub Jelinek
  2014-11-14 17:30                           ` Jakub Jelinek
  0 siblings, 2 replies; 49+ messages in thread
From: Dodji Seketeli @ 2014-11-14 12:16 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Yury Gribov, Jan Hubicka, Marek Polacek, GCC Patches

Jakub Jelinek <jakub@redhat.com> writes:

>> I am not sure, but I am wondering if we shouldn't save the previous uid
>> of 'stmt' here before setting it, and then restore it before getting out
>> of this function.
>
> No, gimple uids are AFAIK undefined at the start of passes, passes that use
> them are supposed to initialize them before use (new statements created
> during the pass will get 0 there by default), and don't have to clean them
> up anyway at the end of pass.

Yeah, this is what I figured by grepping other passes, but I wasn't sure
:-)

Maybe I should follow up with a doc patch for the (otherwise very terse)
comment of gimple_set_uid and gimple_uid accessors.

Thanks.

-- 
		Dodji

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize ASAN_CHECK checks
  2014-11-14 12:16                         ` Dodji Seketeli
@ 2014-11-14 12:18                           ` Jakub Jelinek
  2014-11-14 12:28                             ` Richard Biener
  2014-11-14 17:30                           ` Jakub Jelinek
  1 sibling, 1 reply; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-14 12:18 UTC (permalink / raw)
  To: Dodji Seketeli, Richard Biener
  Cc: Yury Gribov, Jan Hubicka, Marek Polacek, GCC Patches

On Fri, Nov 14, 2014 at 01:06:52PM +0100, Dodji Seketeli wrote:
> Jakub Jelinek <jakub@redhat.com> writes:
> 
> >> I am not sure, but I am wondering if we shouldn't save the previous uid
> >> of 'stmt' here before setting it, and then restore it before getting out
> >> of this function.
> >
> > No, gimple uids are AFAIK undefined at the start of passes, passes that use
> > them are supposed to initialize them before use (new statements created
> > during the pass will get 0 there by default), and don't have to clean them
> > up anyway at the end of pass.
> 
> Yeah, this is what I figured by grepping other passes, but I wasn't sure
> :-)
> 
> Maybe I should follow up with a doc patch for the (otherwise very terse)
> comment of gimple_set_uid and gimple_uid accessors.

That would be indeed nice (similarly for other stuff that we expect to be
undefined on pass boundaries, or expect to be in certain state at pass
boundaries; in the former case set before uses, and don't care about what
state we leave it in, in the latter case can assume some state first (say 0)
and have to put it back into the same state).  There are various visited
flags and the like, Richard, any ideas what other things might be nice to
document?

	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize ASAN_CHECK checks
  2014-11-14 12:18                           ` Jakub Jelinek
@ 2014-11-14 12:28                             ` Richard Biener
  2014-11-14 13:06                               ` Jakub Jelinek
  0 siblings, 1 reply; 49+ messages in thread
From: Richard Biener @ 2014-11-14 12:28 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Dodji Seketeli, Yury Gribov, Jan Hubicka, Marek Polacek, GCC Patches

On Fri, 14 Nov 2014, Jakub Jelinek wrote:

> On Fri, Nov 14, 2014 at 01:06:52PM +0100, Dodji Seketeli wrote:
> > Jakub Jelinek <jakub@redhat.com> writes:
> > 
> > >> I am not sure, but I am wondering if we shouldn't save the previous uid
> > >> of 'stmt' here before setting it, and then restore it before getting out
> > >> of this function.
> > >
> > > No, gimple uids are AFAIK undefined at the start of passes, passes that use
> > > them are supposed to initialize them before use (new statements created
> > > during the pass will get 0 there by default), and don't have to clean them
> > > up anyway at the end of pass.
> > 
> > Yeah, this is what I figured by grepping other passes, but I wasn't sure
> > :-)
> > 
> > Maybe I should follow up with a doc patch for the (otherwise very terse)
> > comment of gimple_set_uid and gimple_uid accessors.
> 
> That would be indeed nice (similarly for other stuff that we expect to be
> undefined on pass boundaries, or expect to be in certain state at pass
> boundaries; in the former case set before uses, and don't care about what
> state we leave it in, in the latter case can assume some state first (say 0)
> and have to put it back into the same state).  There are various visited
> flags and the like, Richard, any ideas what other things might be nice to
> document?

aux pointers on CFG structures which I believe have to be cleared
after each pass that uses them (maybe already documented).

Nothing else off the top of my head.

Richard.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize ASAN_CHECK checks
  2014-11-14 12:28                             ` Richard Biener
@ 2014-11-14 13:06                               ` Jakub Jelinek
  0 siblings, 0 replies; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-14 13:06 UTC (permalink / raw)
  To: Richard Biener
  Cc: Dodji Seketeli, Yury Gribov, Jan Hubicka, Marek Polacek, GCC Patches

On Fri, Nov 14, 2014 at 01:16:36PM +0100, Richard Biener wrote:
> > That would be indeed nice (similarly for other stuff that we expect to be
> > undefined on pass boundaries, or expect to be in certain state at pass
> > boundaries; in the former case set before uses, and don't care about what
> > state we leave it in, in the latter case can assume some state first (say 0)
> > and have to put it back into the same state).  There are various visited
> > flags and the like, Richard, any ideas what other things might be nice to
> > document?
> 
> aux pointers on CFG structures which I believe have to be cleared
> after each pass that uses them (maybe already documented).
> 
> Nothing else off the top of my head.

There is at least gimple_plf GF_PLF_{1,2} too, gimple_visited_p, BB_VISITED, ... 

	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Optimize ASAN_CHECK checks
  2014-11-14 12:16                         ` Dodji Seketeli
  2014-11-14 12:18                           ` Jakub Jelinek
@ 2014-11-14 17:30                           ` Jakub Jelinek
  1 sibling, 0 replies; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-14 17:30 UTC (permalink / raw)
  To: Dodji Seketeli; +Cc: Yury Gribov, Jan Hubicka, Marek Polacek, GCC Patches

On Fri, Nov 14, 2014 at 01:06:52PM +0100, Dodji Seketeli wrote:
> Jakub Jelinek <jakub@redhat.com> writes:
> 
> >> I am not sure, but I am wondering if we shouldn't save the previous uid
> >> of 'stmt' here before setting it, and then restore it before getting out
> >> of this function.
> >
> > No, gimple uids are AFAIK undefined at the start of passes, passes that use
> > them are supposed to initialize them before use (new statements created
> > during the pass will get 0 there by default), and don't have to clean them
> > up anyway at the end of pass.
> 
> Yeah, this is what I figured by grepping other passes, but I wasn't sure
> :-)

Here is what I've committed after another bootstrap/regtest.

2014-11-14  Jakub Jelinek  <jakub@redhat.com>
	    Marek Polacek  <polacek@redhat.com>

	* sanopt.c: Include tree-ssa-operands.h.
	(struct sanopt_info): Add has_freeing_call_p,
	has_freeing_call_computed_p, imm_dom_path_with_freeing_call_p,
	imm_dom_path_with_freeing_call_computed_p, freeing_call_events,
	being_visited_p fields.
	(struct sanopt_ctx): Add asan_check_map field.
	(imm_dom_path_with_freeing_call, maybe_optimize_ubsan_null_ifn,
	maybe_optimize_asan_check_ifn): New functions.
	(sanopt_optimize_walker): Use them, optimize even ASAN_CHECK
	internal calls.
	(pass_sanopt::execute): Call sanopt_optimize even for
	-fsanitize=address.
	* gimple.c (nonfreeing_call_p): Return true for non-ECF_LEAF
	internal calls.

--- gcc/sanopt.c.jj	2014-11-14 15:11:49.202397369 +0100
+++ gcc/sanopt.c	2014-11-14 15:19:40.517912502 +0100
@@ -49,6 +49,7 @@ along with GCC; see the file COPYING3.
 #include "langhooks.h"
 #include "ubsan.h"
 #include "params.h"
+#include "tree-ssa-operands.h"
 
 
 /* This is used to carry information about basic blocks.  It is
@@ -56,7 +57,30 @@ along with GCC; see the file COPYING3.
 
 struct sanopt_info
 {
-  /* True if this BB has been visited.  */
+  /* True if this BB might call (directly or indirectly) free/munmap
+     or similar operation.  */
+  bool has_freeing_call_p;
+
+  /* True if HAS_FREEING_CALL_P flag has been computed.  */
+  bool has_freeing_call_computed_p;
+
+  /* True if there is a block with HAS_FREEING_CALL_P flag set
+     on any path between an immediate dominator of BB, denoted
+     imm(BB), and BB.  */
+  bool imm_dom_path_with_freeing_call_p;
+
+  /* True if IMM_DOM_PATH_WITH_FREEING_CALL_P has been computed.  */
+  bool imm_dom_path_with_freeing_call_computed_p;
+
+  /* Number of possibly freeing calls encountered in this bb
+     (so far).  */
+  uint64_t freeing_call_events;
+
+  /* True if BB is currently being visited during computation
+     of IMM_DOM_PATH_WITH_FREEING_CALL_P flag.  */
+  bool being_visited_p;
+
+  /* True if this BB has been visited in the dominator walk.  */
   bool visited_p;
 };
 
@@ -69,131 +93,387 @@ struct sanopt_ctx
      a vector of UBSAN_NULL call statements that check this pointer.  */
   hash_map<tree, auto_vec<gimple> > null_check_map;
 
+  /* This map maps a pointer (the second argument of ASAN_CHECK) to
+     a vector of ASAN_CHECK call statements that check the access.  */
+  hash_map<tree, auto_vec<gimple> > asan_check_map;
+
   /* Number of IFN_ASAN_CHECK statements.  */
   int asan_num_accesses;
 };
 
 
-/* Try to optimize away redundant UBSAN_NULL checks.
-   
+/* Return true if there might be any call to free/munmap operation
+   on any path in between DOM (which should be imm(BB)) and BB.  */
+
+static bool
+imm_dom_path_with_freeing_call (basic_block bb, basic_block dom)
+{
+  sanopt_info *info = (sanopt_info *) bb->aux;
+  edge e;
+  edge_iterator ei;
+
+  if (info->imm_dom_path_with_freeing_call_computed_p)
+    return info->imm_dom_path_with_freeing_call_p;
+
+  info->being_visited_p = true;
+
+  FOR_EACH_EDGE (e, ei, bb->preds)
+    {
+      sanopt_info *pred_info = (sanopt_info *) e->src->aux;
+
+      if (e->src == dom)
+	continue;
+
+      if ((pred_info->imm_dom_path_with_freeing_call_computed_p
+	  && pred_info->imm_dom_path_with_freeing_call_p)
+	  || (pred_info->has_freeing_call_computed_p
+	      && pred_info->has_freeing_call_p))
+	{
+	  info->imm_dom_path_with_freeing_call_computed_p = true;
+	  info->imm_dom_path_with_freeing_call_p = true;
+	  info->being_visited_p = false;
+	  return true;
+	}
+    }
+
+  FOR_EACH_EDGE (e, ei, bb->preds)
+    {
+      sanopt_info *pred_info = (sanopt_info *) e->src->aux;
+
+      if (e->src == dom)
+	continue;
+
+      if (pred_info->has_freeing_call_computed_p)
+	continue;
+
+      gimple_stmt_iterator gsi;
+      for (gsi = gsi_start_bb (e->src); !gsi_end_p (gsi); gsi_next (&gsi))
+	{
+	  gimple stmt = gsi_stmt (gsi);
+
+	  if (is_gimple_call (stmt) && !nonfreeing_call_p (stmt))
+	    {
+	      pred_info->has_freeing_call_p = true;
+	      break;
+	    }
+	}
+
+      pred_info->has_freeing_call_computed_p = true;
+      if (pred_info->has_freeing_call_p)
+	{
+	  info->imm_dom_path_with_freeing_call_computed_p = true;
+	  info->imm_dom_path_with_freeing_call_p = true;
+	  info->being_visited_p = false;
+	  return true;
+	}
+    }
+
+  FOR_EACH_EDGE (e, ei, bb->preds)
+    {
+      if (e->src == dom)
+	continue;
+
+      basic_block src;
+      for (src = e->src; src != dom; )
+	{
+	  sanopt_info *pred_info = (sanopt_info *) src->aux;
+	  if (pred_info->being_visited_p)
+	    break;
+	  basic_block imm = get_immediate_dominator (CDI_DOMINATORS, src);
+	  if (imm_dom_path_with_freeing_call (src, imm))
+	    {
+	      info->imm_dom_path_with_freeing_call_computed_p = true;
+	      info->imm_dom_path_with_freeing_call_p = true;
+	      info->being_visited_p = false;
+	      return true;
+	    }
+	  src = imm;
+	}
+    }
+
+  info->imm_dom_path_with_freeing_call_computed_p = true;
+  info->imm_dom_path_with_freeing_call_p = false;
+  info->being_visited_p = false;
+  return false;
+}
+
+/* Optimize away redundant UBSAN_NULL calls.  */
+
+static bool
+maybe_optimize_ubsan_null_ifn (struct sanopt_ctx *ctx, gimple stmt)
+{
+  gcc_assert (gimple_call_num_args (stmt) == 3);
+  tree ptr = gimple_call_arg (stmt, 0);
+  tree cur_align = gimple_call_arg (stmt, 2);
+  gcc_assert (TREE_CODE (cur_align) == INTEGER_CST);
+  bool remove = false;
+
+  auto_vec<gimple> &v = ctx->null_check_map.get_or_insert (ptr);
+  if (v.is_empty ())
+    {
+      /* For this PTR we don't have any UBSAN_NULL stmts recorded, so there's
+	 nothing to optimize yet.  */
+      v.safe_push (stmt);
+      return false;
+    }
+
+  /* We already have recorded a UBSAN_NULL check for this pointer. Perhaps we
+     can drop this one.  But only if this check doesn't specify stricter
+     alignment.  */
+  while (!v.is_empty ())
+    {
+      gimple g = v.last ();
+      /* Remove statements for BBs that have been already processed.  */
+      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
+      if (si->visited_p)
+	{
+	  v.pop ();
+	  continue;
+	}
+
+      /* At this point we shouldn't have any statements that aren't dominating
+	 the current BB.  */
+      tree align = gimple_call_arg (g, 2);
+      int kind = tree_to_shwi (gimple_call_arg (g, 1));
+      /* If this is a NULL pointer check where we had segv anyway, we can
+	 remove it.  */
+      if (integer_zerop (align)
+	  && (kind == UBSAN_LOAD_OF
+	      || kind == UBSAN_STORE_OF
+	      || kind == UBSAN_MEMBER_ACCESS))
+	remove = true;
+      /* Otherwise remove the check in non-recovering mode, or if the
+	 stmts have same location.  */
+      else if (integer_zerop (align))
+	remove = (flag_sanitize_recover & SANITIZE_NULL) == 0
+		 || flag_sanitize_undefined_trap_on_error
+		 || gimple_location (g) == gimple_location (stmt);
+      else if (tree_int_cst_le (cur_align, align))
+	remove = (flag_sanitize_recover & SANITIZE_ALIGNMENT) == 0
+		 || flag_sanitize_undefined_trap_on_error
+		 || gimple_location (g) == gimple_location (stmt);
+      if (!remove && gimple_bb (g) == gimple_bb (stmt)
+	  && tree_int_cst_compare (cur_align, align) == 0)
+	v.pop ();
+      break;
+    }
+
+  if (!remove)
+    v.safe_push (stmt);
+  return remove;
+}
+
+/* Optimize away redundant ASAN_CHECK calls.  */
+
+static bool
+maybe_optimize_asan_check_ifn (struct sanopt_ctx *ctx, gimple stmt)
+{
+  gcc_assert (gimple_call_num_args (stmt) == 4);
+  tree ptr = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
+  basic_block bb = gimple_bb (stmt);
+  sanopt_info *info = (sanopt_info *) bb->aux;
+
+  if (TREE_CODE (len) != INTEGER_CST)
+    return false;
+  if (integer_zerop (len))
+    return false;
+
+  gimple_set_uid (stmt, info->freeing_call_events);
+
+  auto_vec<gimple> &v = ctx->asan_check_map.get_or_insert (ptr);
+  if (v.is_empty ())
+    {
+      /* For this PTR we don't have any ASAN_CHECK stmts recorded, so there's
+	 nothing to optimize yet.  */
+      v.safe_push (stmt);
+      return false;
+    }
+
+  /* We already have recorded a ASAN_CHECK check for this pointer.  Perhaps
+     we can drop this one.  But only if this check doesn't specify larger
+     size.  */
+  while (!v.is_empty ())
+    {
+      gimple g = v.last ();
+      /* Remove statements for BBs that have been already processed.  */
+      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
+      if (si->visited_p)
+	v.pop ();
+      else
+	break;
+    }
+
+  unsigned int i;
+  gimple g;
+  gimple to_pop = NULL;
+  bool remove = false;
+  basic_block last_bb = bb;
+  bool cleanup = false;
+
+  FOR_EACH_VEC_ELT_REVERSE (v, i, g)
+    {
+      basic_block gbb = gimple_bb (g);
+      sanopt_info *si = (sanopt_info *) gbb->aux;
+      if (gimple_uid (g) < si->freeing_call_events)
+	{
+	  /* If there is a potentially freeing call after g in gbb, we should
+	     remove it from the vector, can't use in optimization.  */
+	  cleanup = true;
+	  continue;
+	}
+
+      if (TREE_CODE (len) != INTEGER_CST)
+	{
+	  /* If there is some stmts not followed by freeing call event
+	     for ptr already in the current bb, no need to insert anything.
+	     Non-constant len is treated just as length 1.  */
+	  if (gbb == bb)
+	    return false;
+	  break;
+	}
+
+      tree glen = gimple_call_arg (g, 2);
+      /* If we've checked only smaller length than we want to check now,
+	 we can't remove the current stmt.  If g is in the same basic block,
+	 we want to remove it though, as the current stmt is better.  */
+      if (tree_int_cst_lt (glen, len))
+	{
+	  if (gbb == bb)
+	    {
+	      to_pop = g;
+	      cleanup = true;
+	    }
+	  continue;
+	}
+
+      while (last_bb != gbb)
+	{
+	  /* Paths from last_bb to bb have been checked before.
+	     gbb is necessarily a dominator of last_bb, but not necessarily
+	     immediate dominator.  */
+	  if (((sanopt_info *) last_bb->aux)->freeing_call_events)
+	    break;
+
+	  basic_block imm = get_immediate_dominator (CDI_DOMINATORS, last_bb);
+	  gcc_assert (imm);
+	  if (imm_dom_path_with_freeing_call (last_bb, imm))
+	    break;
+
+	  last_bb = imm;
+	}
+      if (last_bb == gbb)
+	remove = true;
+      break;
+    }
+
+  if (cleanup)
+    {
+      unsigned int j = 0, l = v.length ();
+      for (i = 0; i < l; i++)
+	if (v[i] != to_pop
+	    && (gimple_uid (v[i])
+		== ((sanopt_info *)
+		    gimple_bb (v[i])->aux)->freeing_call_events))
+	  {
+	    if (i != j)
+	      v[j] = v[i];
+	    j++;
+	  }
+      v.truncate (j);
+    }
+
+  if (!remove)
+    v.safe_push (stmt);
+  return remove;
+}
+
+/* Try to optimize away redundant UBSAN_NULL and ASAN_CHECK calls.
+
    We walk blocks in the CFG via a depth first search of the dominator
-   tree; we push unique UBSAN_NULL statements into a vector in the
-   NULL_CHECK_MAP as we enter the blocks.  When leaving a block, we
-   mark the block as visited; then when checking the statements in the
-   vector, we ignore statements that are coming from already visited
-   blocks, because these cannot dominate anything anymore.
-   CTX is a sanopt context.  */
+   tree; we push unique UBSAN_NULL or ASAN_CHECK statements into a vector
+   in the NULL_CHECK_MAP or ASAN_CHECK_MAP hash maps as we enter the
+   blocks.  When leaving a block, we mark the block as visited; then
+   when checking the statements in the vector, we ignore statements that
+   are coming from already visited blocks, because these cannot dominate
+   anything anymore.  CTX is a sanopt context.  */
 
 static void
 sanopt_optimize_walker (basic_block bb, struct sanopt_ctx *ctx)
 {
   basic_block son;
   gimple_stmt_iterator gsi;
+  sanopt_info *info = (sanopt_info *) bb->aux;
+  bool asan_check_optimize
+    = (flag_sanitize & SANITIZE_ADDRESS)
+      && ((flag_sanitize & flag_sanitize_recover
+	   & SANITIZE_KERNEL_ADDRESS) == 0);
 
   for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
     {
       gimple stmt = gsi_stmt (gsi);
       bool remove = false;
 
-      if (is_gimple_call (stmt)
-	  && gimple_call_internal_p (stmt))
+      if (!is_gimple_call (stmt))
+	{
+	  /* Handle asm volatile or asm with "memory" clobber
+	     the same as potentionally freeing call.  */
+	  if (gimple_code (stmt) == GIMPLE_ASM
+	      && asan_check_optimize
+	      && (gimple_asm_clobbers_memory_p (stmt)
+		  || gimple_asm_volatile_p (stmt)))
+	    info->freeing_call_events++;
+	  gsi_next (&gsi);
+	  continue;
+	}
+
+      if (asan_check_optimize && !nonfreeing_call_p (stmt))
+	info->freeing_call_events++;
+
+      if (gimple_call_internal_p (stmt))
 	switch (gimple_call_internal_fn (stmt))
 	  {
 	  case IFN_UBSAN_NULL:
-	    {
-	      gcc_assert (gimple_call_num_args (stmt) == 3);
-	      tree ptr = gimple_call_arg (stmt, 0);
-	      tree cur_align = gimple_call_arg (stmt, 2);
-	      gcc_assert (TREE_CODE (cur_align) == INTEGER_CST);
-
-	      auto_vec<gimple> &v = ctx->null_check_map.get_or_insert (ptr);
-	      if (v.is_empty ())
-		/* For this PTR we don't have any UBSAN_NULL stmts
-		   recorded, so there's nothing to optimize yet.  */
-		v.safe_push (stmt);
-	      else
-		{
-		  /* We already have recorded a UBSAN_NULL check
-		     for this pointer.  Perhaps we can drop this one.
-		     But only if this check doesn't specify stricter
-		     alignment.  */
-		  while (!v.is_empty ())
-		    {
-		      gimple g = v.last ();
-		      /* Remove statements for BBs that have been
-			 already processed.  */
-		      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
-		      if (si->visited_p)
-			v.pop ();
-		      else
-			{
-			  /* At this point we shouldn't have any statements
-			     that aren't dominating the current BB.  */
-			  tree align = gimple_call_arg (g, 2);
-			  int kind = tree_to_shwi (gimple_call_arg (g, 1));
-			  /* If this is a NULL pointer check where we had segv
-			     anyway, we can remove it.  */
-			  if (integer_zerop (align)
-			      && (kind == UBSAN_LOAD_OF
-				  || kind == UBSAN_STORE_OF
-				  || kind == UBSAN_MEMBER_ACCESS))
-			    remove = true;
-			  /* Otherwise remove the check in non-recovering
-			     mode, or if the stmts have same location.  */
-			  else if (integer_zerop (align))
-			    remove = !(flag_sanitize_recover & SANITIZE_NULL)
-				     || flag_sanitize_undefined_trap_on_error
-				     || gimple_location (g)
-					== gimple_location (stmt);
-			  else if (tree_int_cst_le (cur_align, align))
-			    remove = !(flag_sanitize_recover
-				       & SANITIZE_ALIGNMENT)
-				     || flag_sanitize_undefined_trap_on_error
-				     || gimple_location (g)
-					== gimple_location (stmt);
-			  if (!remove && gimple_bb (g) == gimple_bb (stmt)
-			      && tree_int_cst_compare (cur_align, align) == 0)
-			    v.pop ();
-			  break;
-			}
-		    }
-
-		  if (remove)
-		    {
-		      /* Drop this check.  */
-		      if (dump_file && (dump_flags & TDF_DETAILS))
-			{
-			  fprintf (dump_file, "Optimizing out\n  ");
-			  print_gimple_stmt (dump_file, stmt, 0,
-					     dump_flags);
-			  fprintf (dump_file, "\n");
-			}
-		      gsi_remove (&gsi, true);
-		    }
-		  else
-		    v.safe_push (stmt);
-		  }
-	    }
+	    remove = maybe_optimize_ubsan_null_ifn (ctx, stmt);
+	    break;
 	  case IFN_ASAN_CHECK:
-	    ctx->asan_num_accesses++;
+	    if (asan_check_optimize)
+	      remove = maybe_optimize_asan_check_ifn (ctx, stmt);
+	    if (!remove)
+	      ctx->asan_num_accesses++;
 	    break;
 	  default:
 	    break;
 	  }
 
-      /* If we were able to remove the current statement, gsi_remove
-	 already pointed us to the next statement.  */
-      if (!remove)
+      if (remove)
+	{
+	  /* Drop this check.  */
+	  if (dump_file && (dump_flags & TDF_DETAILS))
+	    {
+	      fprintf (dump_file, "Optimizing out\n  ");
+	      print_gimple_stmt (dump_file, stmt, 0, dump_flags);
+	      fprintf (dump_file, "\n");
+	    }
+	  unlink_stmt_vdef (stmt);
+	  gsi_remove (&gsi, true);
+	}
+      else
 	gsi_next (&gsi);
     }
 
+  if (asan_check_optimize)
+    {
+      info->has_freeing_call_p = info->freeing_call_events != 0;
+      info->has_freeing_call_computed_p = true;
+    }
+
   for (son = first_dom_son (CDI_DOMINATORS, bb);
        son;
        son = next_dom_son (CDI_DOMINATORS, son))
     sanopt_optimize_walker (son, ctx);
 
   /* We're leaving this BB, so mark it to that effect.  */
-  sanopt_info *info = (sanopt_info *) bb->aux;
   info->visited_p = true;
 }
 
@@ -259,7 +539,8 @@ pass_sanopt::execute (function *fun)
 
   /* Try to remove redundant checks.  */
   if (optimize
-      && (flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT)))
+      && (flag_sanitize
+	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_ADDRESS)))
     asan_num_accesses = sanopt_optimize (fun);
   else if (flag_sanitize & SANITIZE_ADDRESS)
     {
--- gcc/gimple.c.jj	2014-11-14 15:11:46.673442979 +0100
+++ gcc/gimple.c	2014-11-14 15:13:59.287047322 +0100
@@ -2538,6 +2538,9 @@ nonfreeing_call_p (gimple call)
 	default:
 	  return true;
       }
+  else if (gimple_call_internal_p (call)
+	   && gimple_call_flags (call) & ECF_LEAF)
+    return true;
 
   return false;
 }


	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Propagate nonfreeing_call_p using ipa-pure-const (take 2)
  2014-11-13 12:05                                     ` Jakub Jelinek
@ 2014-11-14 17:44                                       ` Jakub Jelinek
  2014-11-14 17:51                                         ` Jan Hubicka
  0 siblings, 1 reply; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-14 17:44 UTC (permalink / raw)
  To: Jan Hubicka, Richard Biener
  Cc: Maxim Ostapenko, Yury Gribov, Marek Polacek, GCC Patches,
	Konstantin Serebryany, Maxim Ostapenko

On Thu, Nov 13, 2014 at 12:52:21PM +0100, Jakub Jelinek wrote:
> On Thu, Nov 13, 2014 at 09:39:42AM +0100, Jakub Jelinek wrote:
> > What about the:
> > > > I wonder if the nonfreeing_call_p function shouldn't be moved elsewhere
> > > > though (suggestion where), so that gimple.c doesn't need the cgraph
> > > > includes.
> > question though (maybe it is more on Richard)?
> 
> Tried richi's suggested cgraph.[ch], but that meant all users of
> nonfreeing_call_p had to start including all of ipa-ref.h, lto-streamer.h
> and cgraph.h, so it is probably better to keep it in gimple.[ch].

Here is a new version, which also handles IFN_ABNORMAL_DISPATCHER as
nonfreeing, it doesn't really call anything, just connect abnormal edges.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

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.

--- gcc/cgraph.c.jj	2014-11-14 15:11:46.691442654 +0100
+++ gcc/cgraph.c	2014-11-14 15:20:25.642079723 +0100
@@ -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))
--- gcc/ipa-pure-const.c.jj	2014-11-14 15:11:46.640443574 +0100
+++ gcc/ipa-pure-const.c	2014-11-14 15:20:25.643079828 +0100
@@ -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 ca
       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 ca
 	    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,
 	    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,
 	    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
   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
 	   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)
 	    {
--- gcc/cgraph.h.jj	2014-11-14 15:11:46.692442636 +0100
+++ gcc/cgraph.h	2014-11-14 15:20:25.644079928 +0100
@@ -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
--- gcc/gimple.c.jj	2014-11-14 15:13:59.287047322 +0100
+++ gcc/gimple.c	2014-11-14 15:21:35.828819194 +0100
@@ -58,6 +58,9 @@ along with GCC; see the file COPYING3.
 #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.
--- gcc/lto-cgraph.c.jj	2014-11-14 15:11:46.429447379 +0100
+++ gcc/lto-cgraph.c	2014-11-14 15:20:25.646080120 +0100
@@ -562,6 +562,7 @@ lto_output_node (struct lto_simple_outpu
   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_de
   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);


	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Propagate nonfreeing_call_p using ipa-pure-const (take 2)
  2014-11-14 17:44                                       ` Jakub Jelinek
@ 2014-11-14 17:51                                         ` Jan Hubicka
  0 siblings, 0 replies; 49+ messages in thread
From: Jan Hubicka @ 2014-11-14 17:51 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Jan Hubicka, Richard Biener, Maxim Ostapenko, Yury Gribov,
	Marek Polacek, GCC Patches, Konstantin Serebryany,
	Maxim Ostapenko

> On Thu, Nov 13, 2014 at 12:52:21PM +0100, Jakub Jelinek wrote:
> > On Thu, Nov 13, 2014 at 09:39:42AM +0100, Jakub Jelinek wrote:
> > > What about the:
> > > > > I wonder if the nonfreeing_call_p function shouldn't be moved elsewhere
> > > > > though (suggestion where), so that gimple.c doesn't need the cgraph
> > > > > includes.
> > > question though (maybe it is more on Richard)?
> > 
> > Tried richi's suggested cgraph.[ch], but that meant all users of
> > nonfreeing_call_p had to start including all of ipa-ref.h, lto-streamer.h
> > and cgraph.h, so it is probably better to keep it in gimple.[ch].
> 
> Here is a new version, which also handles IFN_ABNORMAL_DISPATCHER as
> nonfreeing, it doesn't really call anything, just connect abnormal edges.
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> 
> 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.

OK,
thanks
Honza

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH] Enhance ASAN_CHECK optimization
  2014-11-12 10:35               ` Jakub Jelinek
  2014-11-12 11:12                 ` Yury Gribov
@ 2014-11-25 17:26                 ` Yury Gribov
  2014-11-26  9:59                   ` Jakub Jelinek
  2014-12-03  8:09                   ` [PATCHv2] " Yury Gribov
  1 sibling, 2 replies; 49+ messages in thread
From: Yury Gribov @ 2014-11-25 17:26 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Marek Polacek, GCC Patches, Konstantin Serebryany

[-- Attachment #1: Type: text/plain, Size: 752 bytes --]

Hi all,

This patch improves current optimization of ASAN_CHECKS performed by 
sanopt pass.  In addition to searching the sanitized pointer in 
asan_check_map, it also tries to search for definition of this pointer. 
  This allows more checks to be dropped when definition is not a gimple 
value (e.g. load from struct field) and thus cannot be propagated by 
forwprop.

In my experiments this rarely managed to remove more than 0.5% of 
ASAN_CHECKs but some files got as much as 1% improvement e.g.
* gimple.c: 49 (out of 5293)
* varasm.c: 42 (out of 3678)

For a total it was able to remove 2657 checks in Asan-bootstrapped GCC 
(out of ~500K).

I've Asan-bootstrapped, bootstrapped and regtested on x64.

Is this ok for stage3?

Best regards,
Yury


[-- Attachment #2: asan-opt-1.diff --]
[-- Type: text/x-patch, Size: 9871 bytes --]

From 85f65c403f132245e9efcc8a420269f8d631fae6 Mon Sep 17 00:00:00 2001
From: Yury Gribov <y.gribov@samsung.com>
Date: Tue, 25 Nov 2014 11:49:11 +0300
Subject: [PATCH] 2014-11-25  Yury Gribov  <y.gribov@samsung.com>

gcc/
	* sanopt.c (maybe_get_single_definition): New function.
	(struct tree_map_traits): New struct.
	(struct sanopt_ctx): Use custom traits for asan_check_map.
	(maybe_get_dominating_check): New function.
	(maybe_optimize_ubsan_null_ifn): Move code to
	maybe_get_dominating_check.
	(maybe_optimize_asan_check_ifn): Ditto. Take non-SSA expressions
	into account when optimizing.
	(sanopt_optimize_walker): Do not treat recoverable sanitization
	specially.
---
 gcc/sanopt.c |  194 +++++++++++++++++++++++++++++++++++-----------------------
 1 file changed, 116 insertions(+), 78 deletions(-)

diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index e1d11e0..9fe87de 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -84,6 +84,35 @@ struct sanopt_info
   bool visited_p;
 };
 
+/* If T has a single definition of form T = T2, return T2.  */
+
+static tree
+maybe_get_single_definition (tree t)
+{
+  if (TREE_CODE (t) == SSA_NAME)
+    {
+      gimple g = SSA_NAME_DEF_STMT (t);
+      if (gimple_assign_single_p (g))
+	return gimple_assign_rhs1 (g);
+    }
+  return NULL_TREE;
+}
+
+/* Traits class for tree hash maps below.  */
+
+struct tree_map_traits : default_hashmap_traits
+{
+  static inline hashval_t hash (const_tree ref)
+    {
+      return iterative_hash_expr (ref, 0);
+    }
+
+  static inline bool equal_keys (const_tree ref1, const_tree ref2)
+    {
+      return operand_equal_p (ref1, ref2, 0);
+    }
+}; 
+
 /* This is used to carry various hash maps and variables used
    in sanopt_optimize_walker.  */
 
@@ -95,7 +124,7 @@ struct sanopt_ctx
 
   /* This map maps a pointer (the second argument of ASAN_CHECK) to
      a vector of ASAN_CHECK call statements that check the access.  */
-  hash_map<tree, auto_vec<gimple> > asan_check_map;
+  hash_map<tree, auto_vec<gimple>, tree_map_traits> asan_check_map;
 
   /* Number of IFN_ASAN_CHECK statements.  */
   int asan_num_accesses;
@@ -197,6 +226,24 @@ imm_dom_path_with_freeing_call (basic_block bb, basic_block dom)
   return false;
 }
 
+/* Get the first dominating check from the list of stored checks.
+   Non-dominating checks are silently dropped.  */
+
+static gimple
+maybe_get_dominating_check (auto_vec<gimple> &v)
+{
+  for (; !v.is_empty (); v.pop ())
+    {
+      gimple g = v.last ();
+      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
+      if (!si->visited_p)
+	/* At this point we shouldn't have any statements
+	   that aren't dominating the current BB.  */
+	return g;
+    }
+  return NULL;
+}
+
 /* Optimize away redundant UBSAN_NULL calls.  */
 
 static bool
@@ -209,7 +256,8 @@ maybe_optimize_ubsan_null_ifn (struct sanopt_ctx *ctx, gimple stmt)
   bool remove = false;
 
   auto_vec<gimple> &v = ctx->null_check_map.get_or_insert (ptr);
-  if (v.is_empty ())
+  gimple g = maybe_get_dominating_check (v);
+  if (!g)
     {
       /* For this PTR we don't have any UBSAN_NULL stmts recorded, so there's
 	 nothing to optimize yet.  */
@@ -220,43 +268,30 @@ maybe_optimize_ubsan_null_ifn (struct sanopt_ctx *ctx, gimple stmt)
   /* We already have recorded a UBSAN_NULL check for this pointer. Perhaps we
      can drop this one.  But only if this check doesn't specify stricter
      alignment.  */
-  while (!v.is_empty ())
-    {
-      gimple g = v.last ();
-      /* Remove statements for BBs that have been already processed.  */
-      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
-      if (si->visited_p)
-	{
-	  v.pop ();
-	  continue;
-	}
 
-      /* At this point we shouldn't have any statements that aren't dominating
-	 the current BB.  */
-      tree align = gimple_call_arg (g, 2);
-      int kind = tree_to_shwi (gimple_call_arg (g, 1));
-      /* If this is a NULL pointer check where we had segv anyway, we can
-	 remove it.  */
-      if (integer_zerop (align)
-	  && (kind == UBSAN_LOAD_OF
-	      || kind == UBSAN_STORE_OF
-	      || kind == UBSAN_MEMBER_ACCESS))
-	remove = true;
-      /* Otherwise remove the check in non-recovering mode, or if the
-	 stmts have same location.  */
-      else if (integer_zerop (align))
-	remove = (flag_sanitize_recover & SANITIZE_NULL) == 0
-		 || flag_sanitize_undefined_trap_on_error
-		 || gimple_location (g) == gimple_location (stmt);
-      else if (tree_int_cst_le (cur_align, align))
-	remove = (flag_sanitize_recover & SANITIZE_ALIGNMENT) == 0
-		 || flag_sanitize_undefined_trap_on_error
-		 || gimple_location (g) == gimple_location (stmt);
-      if (!remove && gimple_bb (g) == gimple_bb (stmt)
-	  && tree_int_cst_compare (cur_align, align) == 0)
-	v.pop ();
-      break;
-    }
+  tree align = gimple_call_arg (g, 2);
+  int kind = tree_to_shwi (gimple_call_arg (g, 1));
+  /* If this is a NULL pointer check where we had segv anyway, we can
+     remove it.  */
+  if (integer_zerop (align)
+      && (kind == UBSAN_LOAD_OF
+	  || kind == UBSAN_STORE_OF
+	  || kind == UBSAN_MEMBER_ACCESS))
+    remove = true;
+  /* Otherwise remove the check in non-recovering mode, or if the
+     stmts have same location.  */
+  else if (integer_zerop (align))
+    remove = (flag_sanitize_recover & SANITIZE_NULL) == 0
+	      || flag_sanitize_undefined_trap_on_error
+	      || gimple_location (g) == gimple_location (stmt);
+  else if (tree_int_cst_le (cur_align, align))
+    remove = (flag_sanitize_recover & SANITIZE_ALIGNMENT) == 0
+	      || flag_sanitize_undefined_trap_on_error
+	      || gimple_location (g) == gimple_location (stmt);
+
+  if (!remove && gimple_bb (g) == gimple_bb (stmt)
+      && tree_int_cst_compare (cur_align, align) == 0)
+    v.pop ();
 
   if (!remove)
     v.safe_push (stmt);
@@ -281,37 +316,46 @@ maybe_optimize_asan_check_ifn (struct sanopt_ctx *ctx, gimple stmt)
 
   gimple_set_uid (stmt, info->freeing_call_events);
 
-  auto_vec<gimple> &v = ctx->asan_check_map.get_or_insert (ptr);
-  if (v.is_empty ())
+  auto_vec<gimple> *ptr_checks = &ctx->asan_check_map.get_or_insert (ptr);
+  gimple g = maybe_get_dominating_check (*ptr_checks);
+
+  tree base_addr = maybe_get_single_definition (ptr);
+  auto_vec<gimple> *base_checks = NULL;
+  if (base_addr)
     {
-      /* For this PTR we don't have any ASAN_CHECK stmts recorded, so there's
-	 nothing to optimize yet.  */
-      v.safe_push (stmt);
-      return false;
+      base_checks = &ctx->asan_check_map.get_or_insert (base_addr);
+      /* Original pointer might have been invalidated.  */
+      ptr_checks = ctx->asan_check_map.get (ptr);
     }
 
-  /* We already have recorded a ASAN_CHECK check for this pointer.  Perhaps
-     we can drop this one.  But only if this check doesn't specify larger
-     size.  */
-  while (!v.is_empty ())
+  auto_vec<gimple> *checks = ptr_checks;
+
+  if (!g)
     {
-      gimple g = v.last ();
-      /* Remove statements for BBs that have been already processed.  */
-      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
-      if (si->visited_p)
-	v.pop ();
-      else
-	break;
+      /* Try with base address as well.  */
+      if (base_checks)
+	{
+	  g = maybe_get_dominating_check (*base_checks);
+	  checks = base_checks;
+	}
+      if (!g)
+	{
+	  /* For this PTR we don't have any ASAN_CHECK stmts recorded, so there's
+	     nothing to optimize yet.  */
+	  ptr_checks->safe_push (stmt);
+	  if (base_checks)
+	    base_checks->safe_push (stmt);
+	  return false;
+	}
     }
 
   unsigned int i;
-  gimple g;
   gimple to_pop = NULL;
   bool remove = false;
   basic_block last_bb = bb;
   bool cleanup = false;
 
-  FOR_EACH_VEC_ELT_REVERSE (v, i, g)
+  FOR_EACH_VEC_ELT_REVERSE (*checks, i, g)
     {
       basic_block gbb = gimple_bb (g);
       sanopt_info *si = (sanopt_info *) gbb->aux;
@@ -323,17 +367,9 @@ maybe_optimize_asan_check_ifn (struct sanopt_ctx *ctx, gimple stmt)
 	  continue;
 	}
 
-      if (TREE_CODE (len) != INTEGER_CST)
-	{
-	  /* If there is some stmts not followed by freeing call event
-	     for ptr already in the current bb, no need to insert anything.
-	     Non-constant len is treated just as length 1.  */
-	  if (gbb == bb)
-	    return false;
-	  break;
-	}
-
       tree glen = gimple_call_arg (g, 2);
+      gcc_assert (TREE_CODE (glen) == INTEGER_CST);
+
       /* If we've checked only smaller length than we want to check now,
 	 we can't remove the current stmt.  If g is in the same basic block,
 	 we want to remove it though, as the current stmt is better.  */
@@ -369,22 +405,27 @@ maybe_optimize_asan_check_ifn (struct sanopt_ctx *ctx, gimple stmt)
 
   if (cleanup)
     {
-      unsigned int j = 0, l = v.length ();
+      unsigned int j = 0, l = checks->length ();
       for (i = 0; i < l; i++)
-	if (v[i] != to_pop
-	    && (gimple_uid (v[i])
+	if ((*checks)[i] != to_pop
+	    && (gimple_uid ((*checks)[i])
 		== ((sanopt_info *)
-		    gimple_bb (v[i])->aux)->freeing_call_events))
+		    gimple_bb ((*checks)[i])->aux)->freeing_call_events))
 	  {
 	    if (i != j)
-	      v[j] = v[i];
+	      (*checks)[j] = (*checks)[i];
 	    j++;
 	  }
-      v.truncate (j);
+      checks->truncate (j);
     }
 
   if (!remove)
-    v.safe_push (stmt);
+    {
+      ptr_checks->safe_push (stmt);
+      if (base_checks)
+	base_checks->safe_push (stmt);
+    }
+
   return remove;
 }
 
@@ -404,10 +445,7 @@ sanopt_optimize_walker (basic_block bb, struct sanopt_ctx *ctx)
   basic_block son;
   gimple_stmt_iterator gsi;
   sanopt_info *info = (sanopt_info *) bb->aux;
-  bool asan_check_optimize
-    = (flag_sanitize & SANITIZE_ADDRESS)
-      && ((flag_sanitize & flag_sanitize_recover
-	   & SANITIZE_KERNEL_ADDRESS) == 0);
+  bool asan_check_optimize = (flag_sanitize & SANITIZE_ADDRESS) != 0;
 
   for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
     {
-- 
1.7.9.5


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Enhance ASAN_CHECK optimization
  2014-11-25 17:26                 ` [PATCH] Enhance ASAN_CHECK optimization Yury Gribov
@ 2014-11-26  9:59                   ` Jakub Jelinek
  2014-11-26 18:43                     ` ygribov
  2014-12-03  8:09                   ` [PATCHv2] " Yury Gribov
  1 sibling, 1 reply; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-26  9:59 UTC (permalink / raw)
  To: Yury Gribov; +Cc: Marek Polacek, GCC Patches, Konstantin Serebryany

On Tue, Nov 25, 2014 at 08:06:00PM +0300, Yury Gribov wrote:
> +/* Traits class for tree hash maps below.  */
> +
> +struct tree_map_traits : default_hashmap_traits
> +{
> +  static inline hashval_t hash (const_tree ref)
> +    {
> +      return iterative_hash_expr (ref, 0);
> +    }
> +
> +  static inline bool equal_keys (const_tree ref1, const_tree ref2)
> +    {
> +      return operand_equal_p (ref1, ref2, 0);
> +    }

Formatting.  The {} should be indented like static and return 2 columns to
the right of that.

> @@ -281,37 +316,46 @@ maybe_optimize_asan_check_ifn (struct sanopt_ctx *ctx, gimple stmt)
>  
>    gimple_set_uid (stmt, info->freeing_call_events);
>  
> -  auto_vec<gimple> &v = ctx->asan_check_map.get_or_insert (ptr);
> -  if (v.is_empty ())
> +  auto_vec<gimple> *ptr_checks = &ctx->asan_check_map.get_or_insert (ptr);
> +  gimple g = maybe_get_dominating_check (*ptr_checks);
> +
> +  tree base_addr = maybe_get_single_definition (ptr);
> +  auto_vec<gimple> *base_checks = NULL;
> +  if (base_addr)
>      {
> -      /* For this PTR we don't have any ASAN_CHECK stmts recorded, so there's
> -	 nothing to optimize yet.  */
> -      v.safe_push (stmt);
> -      return false;
> +      base_checks = &ctx->asan_check_map.get_or_insert (base_addr);
> +      /* Original pointer might have been invalidated.  */
> +      ptr_checks = ctx->asan_check_map.get (ptr);
>      }

For base_addr computation, you don't really need g or ptr_checks,
do you?  So why not move the:
  auto_vec<gimple> *ptr_checks = &ctx->asan_check_map.get_or_insert (ptr);
  gimple g = maybe_get_dominating_check (*ptr_checks);
lines below the if?

> @@ -404,10 +445,7 @@ sanopt_optimize_walker (basic_block bb, struct sanopt_ctx *ctx)
>    basic_block son;
>    gimple_stmt_iterator gsi;
>    sanopt_info *info = (sanopt_info *) bb->aux;
> -  bool asan_check_optimize
> -    = (flag_sanitize & SANITIZE_ADDRESS)
> -      && ((flag_sanitize & flag_sanitize_recover
> -	   & SANITIZE_KERNEL_ADDRESS) == 0);
> +  bool asan_check_optimize = (flag_sanitize & SANITIZE_ADDRESS) != 0;
>  
>    for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
>      {

I'm afraid I'm not convinced about this hunk.  If asan (kernel-address) is
recovering, I don't see a difference from not reporting two different
invalid accesses to the same function and not reporting two integer
overflows in the same function, at least if they have different location_t.

	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Enhance ASAN_CHECK optimization
  2014-11-26  9:59                   ` Jakub Jelinek
@ 2014-11-26 18:43                     ` ygribov
  2014-11-26 18:50                       ` Jakub Jelinek
  0 siblings, 1 reply; 49+ messages in thread
From: ygribov @ 2014-11-26 18:43 UTC (permalink / raw)
  To: gcc-patches

> Formatting.  The {} should be indented like static
> and return 2 columns to the right of that.

Right.

> For base_addr computation, you don't really need g or ptr_checks, 
> do you?  So why not move the: 
>   auto_vec<gimple> *ptr_checks = &ctx->asan_check_map.get_or_insert (ptr); 
>   gimple g = maybe_get_dominating_check (*ptr_checks); 
> lines below the if?

I can do this.  But then base_checks would be invalidated when I call
get_or_insert for ptr_checks so I'll still have to hash_map::get.

> If asan (kernel-address) is 
> recovering, I don't see a difference from not reporting two different 
> invalid accesses to the same function and not reporting two integer 
> overflows in the same function, at least if they have different
> location_t.

Ok, agreed. BTW how about replacing '& SANITIZE_KERNEL_ADDRESS' with '&
SANITIZE_ADDRESS'?  I know we do not support recovery for userspace but
having a general enum sounds more logical.

-Y



--
View this message in context: http://gcc.1065356.n5.nabble.com/PATCH-Optimize-UBSAN-NULL-checks-add-sanopt-c-tp1085786p1095527.html
Sent from the gcc - patches mailing list archive at Nabble.com.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Enhance ASAN_CHECK optimization
  2014-11-26 18:43                     ` ygribov
@ 2014-11-26 18:50                       ` Jakub Jelinek
  2014-11-26 18:54                         ` ygribov
  0 siblings, 1 reply; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-26 18:50 UTC (permalink / raw)
  To: ygribov; +Cc: gcc-patches

On Wed, Nov 26, 2014 at 11:21:06AM -0700, ygribov wrote:
> > Formatting.  The {} should be indented like static
> > and return 2 columns to the right of that.
> 
> Right.
> 
> > For base_addr computation, you don't really need g or ptr_checks, 
> > do you?  So why not move the: 
> >   auto_vec<gimple> *ptr_checks = &ctx->asan_check_map.get_or_insert (ptr); 
> >   gimple g = maybe_get_dominating_check (*ptr_checks); 
> > lines below the if?
> 
> I can do this.  But then base_checks would be invalidated when I call
> get_or_insert for ptr_checks so I'll still have to hash_map::get.

You're right.

> > If asan (kernel-address) is 
> > recovering, I don't see a difference from not reporting two different 
> > invalid accesses to the same function and not reporting two integer 
> > overflows in the same function, at least if they have different
> > location_t.
> 
> Ok, agreed. BTW how about replacing '& SANITIZE_KERNEL_ADDRESS' with '&
> SANITIZE_ADDRESS'?  I know we do not support recovery for userspace but
> having a general enum sounds more logical.

Testing SANITIZE_ADDRESS bit in flag_sanitize_recover doesn't make sense,
testing it in flag_sanitize of course does, but for recover you care whether
the SANITIZE_{KERNEL,USER}_ADDRESS bit in flag_sanitize_recover is set
depending on if SANITIZE_{KERNEL,USER}_ADDRESS is set in
flag_sanitize_recover.
So supposedly
((flag_sanitize & flag_sanitize_recover)
 & (SANITIZE_USER_ADDRESS | SANITIZE_KERNEL_ADDRESS)) != 0
is the right check for whether the current address sanitization wants to
recover.

	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Enhance ASAN_CHECK optimization
  2014-11-26 18:50                       ` Jakub Jelinek
@ 2014-11-26 18:54                         ` ygribov
  2014-11-26 19:00                           ` Jakub Jelinek
  0 siblings, 1 reply; 49+ messages in thread
From: ygribov @ 2014-11-26 18:54 UTC (permalink / raw)
  To: gcc-patches

> Testing SANITIZE_ADDRESS bit in flag_sanitize_recover doesn't make sense, 
> testing it in flag_sanitize of course does, but for recover you care
> whether 
> the SANITIZE_{KERNEL,USER}_ADDRESS bit in flag_sanitize_recover is set 
> depending on if SANITIZE_{KERNEL,USER}_ADDRESS is set in 
> flag_sanitize_recover. 

Ok, got it. BTW shouldn't we disable local optimization of ASan checks (in
asan.c) as well? That would be a massive perf hit ...

-Y



--
View this message in context: http://gcc.1065356.n5.nabble.com/PATCH-Optimize-UBSAN-NULL-checks-add-sanopt-c-tp1085786p1095536.html
Sent from the gcc - patches mailing list archive at Nabble.com.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH] Enhance ASAN_CHECK optimization
  2014-11-26 18:54                         ` ygribov
@ 2014-11-26 19:00                           ` Jakub Jelinek
  0 siblings, 0 replies; 49+ messages in thread
From: Jakub Jelinek @ 2014-11-26 19:00 UTC (permalink / raw)
  To: ygribov; +Cc: gcc-patches

On Wed, Nov 26, 2014 at 11:42:57AM -0700, ygribov wrote:
> > Testing SANITIZE_ADDRESS bit in flag_sanitize_recover doesn't make sense, 
> > testing it in flag_sanitize of course does, but for recover you care
> > whether 
> > the SANITIZE_{KERNEL,USER}_ADDRESS bit in flag_sanitize_recover is set 
> > depending on if SANITIZE_{KERNEL,USER}_ADDRESS is set in 
> > flag_sanitize_recover. 
> 
> Ok, got it. BTW shouldn't we disable local optimization of ASan checks (in
> asan.c) as well? That would be a massive perf hit ...

Ah, you're right, we are already doing that.  So let's just optimize always
even when recovering and we'll see if users don't complain too much.

	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCHv2] Enhance ASAN_CHECK optimization
  2014-11-25 17:26                 ` [PATCH] Enhance ASAN_CHECK optimization Yury Gribov
  2014-11-26  9:59                   ` Jakub Jelinek
@ 2014-12-03  8:09                   ` Yury Gribov
  2014-12-03  8:36                     ` Jakub Jelinek
  1 sibling, 1 reply; 49+ messages in thread
From: Yury Gribov @ 2014-12-03  8:09 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Marek Polacek, GCC Patches, Konstantin Serebryany

[-- Attachment #1: Type: text/plain, Size: 879 bytes --]

On 11/25/2014 08:06 PM, Yury Gribov wrote:
> This patch improves current optimization of ASAN_CHECKS performed by
> sanopt pass.  In addition to searching the sanitized pointer in
> asan_check_map, it also tries to search for definition of this pointer.
>   This allows more checks to be dropped when definition is not a gimple
> value (e.g. load from struct field) and thus cannot be propagated by
> forwprop.
>
> In my experiments this rarely managed to remove more than 0.5% of
> ASAN_CHECKs but some files got as much as 1% improvement e.g.
> * gimple.c: 49 (out of 5293)
> * varasm.c: 42 (out of 3678)
>
> For a total it was able to remove 2657 checks in Asan-bootstrapped GCC
> (out of ~500K).

Hi all,

Here is the updated version of the patch.

Asan-bootstrapped, bootstrapped and regtested on x64.

Marek, do you think similar optimization may be useful for UBSan?

-Y


[-- Attachment #2: asan-opt-2.diff --]
[-- Type: text/x-patch, Size: 11512 bytes --]

From 5a9ede4d120ba4e39ca47e212262af53de47eb5a Mon Sep 17 00:00:00 2001
From: Yury Gribov <y.gribov@samsung.com>
Date: Tue, 25 Nov 2014 11:49:11 +0300
Subject: [PATCH] 2014-12-02  Yury Gribov  <y.gribov@samsung.com>

gcc/
	* asan.h (asan_can_optimize_checks): New function.
	* asan.c (update_mem_ref_hash_table): Disable if can't optimize.
	* sanopt.c (maybe_get_single_definition): New function.
	(can_remove_asan_check): Ditto.
	(struct tree_map_traits): New struct.
	(struct sanopt_ctx): Use custom traits for asan_check_map.
	(maybe_get_dominating_check): New function.
	(maybe_optimize_ubsan_null_ifn): Move code to
	maybe_get_dominating_check.
	(maybe_optimize_asan_check_ifn): Move code and take non-SSA expressions
	into account when optimizing.
	(sanopt_optimize_walker): Move code to asan_can_optimize_checks.
---
 gcc/asan.c   |    3 +
 gcc/asan.h   |   11 +++
 gcc/sanopt.c |  233 +++++++++++++++++++++++++++++++++++-----------------------
 3 files changed, 156 insertions(+), 91 deletions(-)

diff --git a/gcc/asan.c b/gcc/asan.c
index a8987b7..8d061cb 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -907,6 +907,9 @@ has_stmt_been_instrumented_p (gimple stmt)
 static void
 update_mem_ref_hash_table (tree ref, HOST_WIDE_INT access_size)
 {
+  if (!asan_can_optimize_checks ())
+    return;
+
   hash_table<asan_mem_ref_hasher> *ht = get_mem_ref_hash_table ();
 
   asan_mem_ref r;
diff --git a/gcc/asan.h b/gcc/asan.h
index f448391..f5e514c 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -103,4 +103,15 @@ asan_intercepted_p (enum built_in_function fcode)
 	 || fcode == BUILT_IN_STRNCMP
 	 || fcode == BUILT_IN_STRNCPY;
 }
+
+/* Return TRUE if redundant ASan checks can be optimized away.  */
+
+static inline bool
+asan_can_optimize_checks ()
+{
+  /* When in recovery mode, we want all errors to be reported.  */
+  return (flag_sanitize & flag_sanitize_recover
+	  & (SANITIZE_USER_ADDRESS | SANITIZE_KERNEL_ADDRESS)) == 0;
+}
+
 #endif /* TREE_ASAN */
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index e1d11e0..268ecb9 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -84,6 +84,35 @@ struct sanopt_info
   bool visited_p;
 };
 
+/* If T has a single definition of form T = T2, return T2.  */
+
+static tree
+maybe_get_single_definition (tree t)
+{
+  if (TREE_CODE (t) == SSA_NAME)
+    {
+      gimple g = SSA_NAME_DEF_STMT (t);
+      if (gimple_assign_single_p (g))
+	return gimple_assign_rhs1 (g);
+    }
+  return NULL_TREE;
+}
+
+/* Traits class for tree hash maps below.  */
+
+struct tree_map_traits : default_hashmap_traits
+{
+  static inline hashval_t hash (const_tree ref)
+  {
+    return iterative_hash_expr (ref, 0);
+  }
+
+  static inline bool equal_keys (const_tree ref1, const_tree ref2)
+  {
+    return operand_equal_p (ref1, ref2, 0);
+  }
+}; 
+
 /* This is used to carry various hash maps and variables used
    in sanopt_optimize_walker.  */
 
@@ -95,7 +124,7 @@ struct sanopt_ctx
 
   /* This map maps a pointer (the second argument of ASAN_CHECK) to
      a vector of ASAN_CHECK call statements that check the access.  */
-  hash_map<tree, auto_vec<gimple> > asan_check_map;
+  hash_map<tree, auto_vec<gimple>, tree_map_traits> asan_check_map;
 
   /* Number of IFN_ASAN_CHECK statements.  */
   int asan_num_accesses;
@@ -197,6 +226,24 @@ imm_dom_path_with_freeing_call (basic_block bb, basic_block dom)
   return false;
 }
 
+/* Get the first dominating check from the list of stored checks.
+   Non-dominating checks are silently dropped.  */
+
+static gimple
+maybe_get_dominating_check (auto_vec<gimple> &v)
+{
+  for (; !v.is_empty (); v.pop ())
+    {
+      gimple g = v.last ();
+      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
+      if (!si->visited_p)
+	/* At this point we shouldn't have any statements
+	   that aren't dominating the current BB.  */
+	return g;
+    }
+  return NULL;
+}
+
 /* Optimize away redundant UBSAN_NULL calls.  */
 
 static bool
@@ -209,7 +256,8 @@ maybe_optimize_ubsan_null_ifn (struct sanopt_ctx *ctx, gimple stmt)
   bool remove = false;
 
   auto_vec<gimple> &v = ctx->null_check_map.get_or_insert (ptr);
-  if (v.is_empty ())
+  gimple g = maybe_get_dominating_check (v);
+  if (!g)
     {
       /* For this PTR we don't have any UBSAN_NULL stmts recorded, so there's
 	 nothing to optimize yet.  */
@@ -220,90 +268,42 @@ maybe_optimize_ubsan_null_ifn (struct sanopt_ctx *ctx, gimple stmt)
   /* We already have recorded a UBSAN_NULL check for this pointer. Perhaps we
      can drop this one.  But only if this check doesn't specify stricter
      alignment.  */
-  while (!v.is_empty ())
-    {
-      gimple g = v.last ();
-      /* Remove statements for BBs that have been already processed.  */
-      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
-      if (si->visited_p)
-	{
-	  v.pop ();
-	  continue;
-	}
 
-      /* At this point we shouldn't have any statements that aren't dominating
-	 the current BB.  */
-      tree align = gimple_call_arg (g, 2);
-      int kind = tree_to_shwi (gimple_call_arg (g, 1));
-      /* If this is a NULL pointer check where we had segv anyway, we can
-	 remove it.  */
-      if (integer_zerop (align)
-	  && (kind == UBSAN_LOAD_OF
-	      || kind == UBSAN_STORE_OF
-	      || kind == UBSAN_MEMBER_ACCESS))
-	remove = true;
-      /* Otherwise remove the check in non-recovering mode, or if the
-	 stmts have same location.  */
-      else if (integer_zerop (align))
-	remove = (flag_sanitize_recover & SANITIZE_NULL) == 0
-		 || flag_sanitize_undefined_trap_on_error
-		 || gimple_location (g) == gimple_location (stmt);
-      else if (tree_int_cst_le (cur_align, align))
-	remove = (flag_sanitize_recover & SANITIZE_ALIGNMENT) == 0
-		 || flag_sanitize_undefined_trap_on_error
-		 || gimple_location (g) == gimple_location (stmt);
-      if (!remove && gimple_bb (g) == gimple_bb (stmt)
-	  && tree_int_cst_compare (cur_align, align) == 0)
-	v.pop ();
-      break;
-    }
+  tree align = gimple_call_arg (g, 2);
+  int kind = tree_to_shwi (gimple_call_arg (g, 1));
+  /* If this is a NULL pointer check where we had segv anyway, we can
+     remove it.  */
+  if (integer_zerop (align)
+      && (kind == UBSAN_LOAD_OF
+	  || kind == UBSAN_STORE_OF
+	  || kind == UBSAN_MEMBER_ACCESS))
+    remove = true;
+  /* Otherwise remove the check in non-recovering mode, or if the
+     stmts have same location.  */
+  else if (integer_zerop (align))
+    remove = (flag_sanitize_recover & SANITIZE_NULL) == 0
+	      || flag_sanitize_undefined_trap_on_error
+	      || gimple_location (g) == gimple_location (stmt);
+  else if (tree_int_cst_le (cur_align, align))
+    remove = (flag_sanitize_recover & SANITIZE_ALIGNMENT) == 0
+	      || flag_sanitize_undefined_trap_on_error
+	      || gimple_location (g) == gimple_location (stmt);
+
+  if (!remove && gimple_bb (g) == gimple_bb (stmt)
+      && tree_int_cst_compare (cur_align, align) == 0)
+    v.pop ();
 
   if (!remove)
     v.safe_push (stmt);
   return remove;
 }
 
-/* Optimize away redundant ASAN_CHECK calls.  */
+/* Returns TRUE if ASan check of length LEN in block BB can be removed
+   if preceded by checks in V.  */
 
 static bool
-maybe_optimize_asan_check_ifn (struct sanopt_ctx *ctx, gimple stmt)
+can_remove_asan_check (auto_vec<gimple> &v, tree len, basic_block bb)
 {
-  gcc_assert (gimple_call_num_args (stmt) == 4);
-  tree ptr = gimple_call_arg (stmt, 1);
-  tree len = gimple_call_arg (stmt, 2);
-  basic_block bb = gimple_bb (stmt);
-  sanopt_info *info = (sanopt_info *) bb->aux;
-
-  if (TREE_CODE (len) != INTEGER_CST)
-    return false;
-  if (integer_zerop (len))
-    return false;
-
-  gimple_set_uid (stmt, info->freeing_call_events);
-
-  auto_vec<gimple> &v = ctx->asan_check_map.get_or_insert (ptr);
-  if (v.is_empty ())
-    {
-      /* For this PTR we don't have any ASAN_CHECK stmts recorded, so there's
-	 nothing to optimize yet.  */
-      v.safe_push (stmt);
-      return false;
-    }
-
-  /* We already have recorded a ASAN_CHECK check for this pointer.  Perhaps
-     we can drop this one.  But only if this check doesn't specify larger
-     size.  */
-  while (!v.is_empty ())
-    {
-      gimple g = v.last ();
-      /* Remove statements for BBs that have been already processed.  */
-      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
-      if (si->visited_p)
-	v.pop ();
-      else
-	break;
-    }
-
   unsigned int i;
   gimple g;
   gimple to_pop = NULL;
@@ -323,17 +323,9 @@ maybe_optimize_asan_check_ifn (struct sanopt_ctx *ctx, gimple stmt)
 	  continue;
 	}
 
-      if (TREE_CODE (len) != INTEGER_CST)
-	{
-	  /* If there is some stmts not followed by freeing call event
-	     for ptr already in the current bb, no need to insert anything.
-	     Non-constant len is treated just as length 1.  */
-	  if (gbb == bb)
-	    return false;
-	  break;
-	}
-
       tree glen = gimple_call_arg (g, 2);
+      gcc_assert (TREE_CODE (glen) == INTEGER_CST);
+
       /* If we've checked only smaller length than we want to check now,
 	 we can't remove the current stmt.  If g is in the same basic block,
 	 we want to remove it though, as the current stmt is better.  */
@@ -383,8 +375,70 @@ maybe_optimize_asan_check_ifn (struct sanopt_ctx *ctx, gimple stmt)
       v.truncate (j);
     }
 
+  return remove;
+}
+
+/* Optimize away redundant ASAN_CHECK calls.  */
+
+static bool
+maybe_optimize_asan_check_ifn (struct sanopt_ctx *ctx, gimple stmt)
+{
+  gcc_assert (gimple_call_num_args (stmt) == 4);
+  tree ptr = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
+  basic_block bb = gimple_bb (stmt);
+  sanopt_info *info = (sanopt_info *) bb->aux;
+
+  if (TREE_CODE (len) != INTEGER_CST)
+    return false;
+  if (integer_zerop (len))
+    return false;
+
+  gimple_set_uid (stmt, info->freeing_call_events);
+
+  auto_vec<gimple> *ptr_checks = &ctx->asan_check_map.get_or_insert (ptr);
+
+  tree base_addr = maybe_get_single_definition (ptr);
+  auto_vec<gimple> *base_checks = NULL;
+  if (base_addr)
+    {
+      base_checks = &ctx->asan_check_map.get_or_insert (base_addr);
+      /* Original pointer might have been invalidated.  */
+      ptr_checks = ctx->asan_check_map.get (ptr);
+    }
+
+  gimple g = maybe_get_dominating_check (*ptr_checks);
+
+  if (!g && base_checks)
+    /* Try with base address as well.  */
+    g = maybe_get_dominating_check (*base_checks);
+
+  if (!g)
+    {
+      /* For this PTR we don't have any ASAN_CHECK stmts recorded, so there's
+	 nothing to optimize yet.  */
+      ptr_checks->safe_push (stmt);
+      if (base_checks)
+	base_checks->safe_push (stmt);
+      return false;
+    }
+
+  bool remove = false;
+
+  if (ptr_checks)
+    remove = can_remove_asan_check (*ptr_checks, len, bb);
+
+  if (!remove && base_checks)
+    /* Try with base address as well.  */
+    remove = can_remove_asan_check (*base_checks, len, bb);
+
   if (!remove)
-    v.safe_push (stmt);
+    {
+      ptr_checks->safe_push (stmt);
+      if (base_checks)
+	base_checks->safe_push (stmt);
+    }
+
   return remove;
 }
 
@@ -404,10 +458,7 @@ sanopt_optimize_walker (basic_block bb, struct sanopt_ctx *ctx)
   basic_block son;
   gimple_stmt_iterator gsi;
   sanopt_info *info = (sanopt_info *) bb->aux;
-  bool asan_check_optimize
-    = (flag_sanitize & SANITIZE_ADDRESS)
-      && ((flag_sanitize & flag_sanitize_recover
-	   & SANITIZE_KERNEL_ADDRESS) == 0);
+  bool asan_check_optimize = asan_can_optimize_checks ();
 
   for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
     {
-- 
1.7.9.5


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCHv2] Enhance ASAN_CHECK optimization
  2014-12-03  8:09                   ` [PATCHv2] " Yury Gribov
@ 2014-12-03  8:36                     ` Jakub Jelinek
  2014-12-03  9:04                       ` Yury Gribov
  0 siblings, 1 reply; 49+ messages in thread
From: Jakub Jelinek @ 2014-12-03  8:36 UTC (permalink / raw)
  To: Yury Gribov; +Cc: Marek Polacek, GCC Patches, Konstantin Serebryany

On Wed, Dec 03, 2014 at 11:09:41AM +0300, Yury Gribov wrote:
> >From 5a9ede4d120ba4e39ca47e212262af53de47eb5a Mon Sep 17 00:00:00 2001
> From: Yury Gribov <y.gribov@samsung.com>
> Date: Tue, 25 Nov 2014 11:49:11 +0300
> Subject: [PATCH] 2014-12-02  Yury Gribov  <y.gribov@samsung.com>
> 
> gcc/
> 	* asan.h (asan_can_optimize_checks): New function.
> 	* asan.c (update_mem_ref_hash_table): Disable if can't optimize.
> 	* sanopt.c (maybe_get_single_definition): New function.
> 	(can_remove_asan_check): Ditto.
> 	(struct tree_map_traits): New struct.
> 	(struct sanopt_ctx): Use custom traits for asan_check_map.
> 	(maybe_get_dominating_check): New function.
> 	(maybe_optimize_ubsan_null_ifn): Move code to
> 	maybe_get_dominating_check.
> 	(maybe_optimize_asan_check_ifn): Move code and take non-SSA expressions
> 	into account when optimizing.
> 	(sanopt_optimize_walker): Move code to asan_can_optimize_checks.

I thought I've agreed that we can optimize checks away for asan even when
recovering.  The patch is ok for trunk anyway, we can perhaps make it
tunable later, or define asan_can_optimize_checks to true unconditionally.

	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCHv2] Enhance ASAN_CHECK optimization
  2014-12-03  8:36                     ` Jakub Jelinek
@ 2014-12-03  9:04                       ` Yury Gribov
  2014-12-03  9:07                         ` Jakub Jelinek
  0 siblings, 1 reply; 49+ messages in thread
From: Yury Gribov @ 2014-12-03  9:04 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Marek Polacek, GCC Patches, Konstantin Serebryany

[-- Attachment #1: Type: text/plain, Size: 463 bytes --]

On 12/03/2014 11:36 AM, Jakub Jelinek wrote:
> I thought I've agreed that we can optimize checks away for asan even when
> recovering.  The patch is ok for trunk anyway, we can perhaps make it
> tunable later, or define asan_can_optimize_checks to true unconditionally.

Snap, looks like my English failed me.  How about this one?  I only 
checked asan.exp but the change looks really trivial (drop 
asan_can_optimize_checks and change asan_check_optimize).

-Y


[-- Attachment #2: asan-opt-3.diff --]
[-- Type: text/x-patch, Size: 10403 bytes --]

From 3bca06f351ed9a7c34f74c30951a7b3cd4907e74 Mon Sep 17 00:00:00 2001
From: Yury Gribov <y.gribov@samsung.com>
Date: Tue, 25 Nov 2014 11:49:11 +0300
Subject: [PATCH] 2014-12-03  Yury Gribov  <y.gribov@samsung.com>

gcc/
	* sanopt.c (maybe_get_single_definition): New function.
	(can_remove_asan_check): Ditto.
	(struct tree_map_traits): New struct.
	(struct sanopt_ctx): Use custom traits for asan_check_map.
	(maybe_get_dominating_check): New function.
	(maybe_optimize_ubsan_null_ifn): Move code to
	maybe_get_dominating_check.
	(maybe_optimize_asan_check_ifn): Move code and take non-SSA expressions
	into account when optimizing.
	(sanopt_optimize_walker): Optimize ASan checks even when
	recovering.
---
 gcc/sanopt.c |  233 +++++++++++++++++++++++++++++++++++-----------------------
 1 file changed, 142 insertions(+), 91 deletions(-)

diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index e1d11e0..e0d3a85 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -84,6 +84,35 @@ struct sanopt_info
   bool visited_p;
 };
 
+/* If T has a single definition of form T = T2, return T2.  */
+
+static tree
+maybe_get_single_definition (tree t)
+{
+  if (TREE_CODE (t) == SSA_NAME)
+    {
+      gimple g = SSA_NAME_DEF_STMT (t);
+      if (gimple_assign_single_p (g))
+	return gimple_assign_rhs1 (g);
+    }
+  return NULL_TREE;
+}
+
+/* Traits class for tree hash maps below.  */
+
+struct tree_map_traits : default_hashmap_traits
+{
+  static inline hashval_t hash (const_tree ref)
+  {
+    return iterative_hash_expr (ref, 0);
+  }
+
+  static inline bool equal_keys (const_tree ref1, const_tree ref2)
+  {
+    return operand_equal_p (ref1, ref2, 0);
+  }
+}; 
+
 /* This is used to carry various hash maps and variables used
    in sanopt_optimize_walker.  */
 
@@ -95,7 +124,7 @@ struct sanopt_ctx
 
   /* This map maps a pointer (the second argument of ASAN_CHECK) to
      a vector of ASAN_CHECK call statements that check the access.  */
-  hash_map<tree, auto_vec<gimple> > asan_check_map;
+  hash_map<tree, auto_vec<gimple>, tree_map_traits> asan_check_map;
 
   /* Number of IFN_ASAN_CHECK statements.  */
   int asan_num_accesses;
@@ -197,6 +226,24 @@ imm_dom_path_with_freeing_call (basic_block bb, basic_block dom)
   return false;
 }
 
+/* Get the first dominating check from the list of stored checks.
+   Non-dominating checks are silently dropped.  */
+
+static gimple
+maybe_get_dominating_check (auto_vec<gimple> &v)
+{
+  for (; !v.is_empty (); v.pop ())
+    {
+      gimple g = v.last ();
+      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
+      if (!si->visited_p)
+	/* At this point we shouldn't have any statements
+	   that aren't dominating the current BB.  */
+	return g;
+    }
+  return NULL;
+}
+
 /* Optimize away redundant UBSAN_NULL calls.  */
 
 static bool
@@ -209,7 +256,8 @@ maybe_optimize_ubsan_null_ifn (struct sanopt_ctx *ctx, gimple stmt)
   bool remove = false;
 
   auto_vec<gimple> &v = ctx->null_check_map.get_or_insert (ptr);
-  if (v.is_empty ())
+  gimple g = maybe_get_dominating_check (v);
+  if (!g)
     {
       /* For this PTR we don't have any UBSAN_NULL stmts recorded, so there's
 	 nothing to optimize yet.  */
@@ -220,90 +268,42 @@ maybe_optimize_ubsan_null_ifn (struct sanopt_ctx *ctx, gimple stmt)
   /* We already have recorded a UBSAN_NULL check for this pointer. Perhaps we
      can drop this one.  But only if this check doesn't specify stricter
      alignment.  */
-  while (!v.is_empty ())
-    {
-      gimple g = v.last ();
-      /* Remove statements for BBs that have been already processed.  */
-      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
-      if (si->visited_p)
-	{
-	  v.pop ();
-	  continue;
-	}
 
-      /* At this point we shouldn't have any statements that aren't dominating
-	 the current BB.  */
-      tree align = gimple_call_arg (g, 2);
-      int kind = tree_to_shwi (gimple_call_arg (g, 1));
-      /* If this is a NULL pointer check where we had segv anyway, we can
-	 remove it.  */
-      if (integer_zerop (align)
-	  && (kind == UBSAN_LOAD_OF
-	      || kind == UBSAN_STORE_OF
-	      || kind == UBSAN_MEMBER_ACCESS))
-	remove = true;
-      /* Otherwise remove the check in non-recovering mode, or if the
-	 stmts have same location.  */
-      else if (integer_zerop (align))
-	remove = (flag_sanitize_recover & SANITIZE_NULL) == 0
-		 || flag_sanitize_undefined_trap_on_error
-		 || gimple_location (g) == gimple_location (stmt);
-      else if (tree_int_cst_le (cur_align, align))
-	remove = (flag_sanitize_recover & SANITIZE_ALIGNMENT) == 0
-		 || flag_sanitize_undefined_trap_on_error
-		 || gimple_location (g) == gimple_location (stmt);
-      if (!remove && gimple_bb (g) == gimple_bb (stmt)
-	  && tree_int_cst_compare (cur_align, align) == 0)
-	v.pop ();
-      break;
-    }
+  tree align = gimple_call_arg (g, 2);
+  int kind = tree_to_shwi (gimple_call_arg (g, 1));
+  /* If this is a NULL pointer check where we had segv anyway, we can
+     remove it.  */
+  if (integer_zerop (align)
+      && (kind == UBSAN_LOAD_OF
+	  || kind == UBSAN_STORE_OF
+	  || kind == UBSAN_MEMBER_ACCESS))
+    remove = true;
+  /* Otherwise remove the check in non-recovering mode, or if the
+     stmts have same location.  */
+  else if (integer_zerop (align))
+    remove = (flag_sanitize_recover & SANITIZE_NULL) == 0
+	      || flag_sanitize_undefined_trap_on_error
+	      || gimple_location (g) == gimple_location (stmt);
+  else if (tree_int_cst_le (cur_align, align))
+    remove = (flag_sanitize_recover & SANITIZE_ALIGNMENT) == 0
+	      || flag_sanitize_undefined_trap_on_error
+	      || gimple_location (g) == gimple_location (stmt);
+
+  if (!remove && gimple_bb (g) == gimple_bb (stmt)
+      && tree_int_cst_compare (cur_align, align) == 0)
+    v.pop ();
 
   if (!remove)
     v.safe_push (stmt);
   return remove;
 }
 
-/* Optimize away redundant ASAN_CHECK calls.  */
+/* Returns TRUE if ASan check of length LEN in block BB can be removed
+   if preceded by checks in V.  */
 
 static bool
-maybe_optimize_asan_check_ifn (struct sanopt_ctx *ctx, gimple stmt)
+can_remove_asan_check (auto_vec<gimple> &v, tree len, basic_block bb)
 {
-  gcc_assert (gimple_call_num_args (stmt) == 4);
-  tree ptr = gimple_call_arg (stmt, 1);
-  tree len = gimple_call_arg (stmt, 2);
-  basic_block bb = gimple_bb (stmt);
-  sanopt_info *info = (sanopt_info *) bb->aux;
-
-  if (TREE_CODE (len) != INTEGER_CST)
-    return false;
-  if (integer_zerop (len))
-    return false;
-
-  gimple_set_uid (stmt, info->freeing_call_events);
-
-  auto_vec<gimple> &v = ctx->asan_check_map.get_or_insert (ptr);
-  if (v.is_empty ())
-    {
-      /* For this PTR we don't have any ASAN_CHECK stmts recorded, so there's
-	 nothing to optimize yet.  */
-      v.safe_push (stmt);
-      return false;
-    }
-
-  /* We already have recorded a ASAN_CHECK check for this pointer.  Perhaps
-     we can drop this one.  But only if this check doesn't specify larger
-     size.  */
-  while (!v.is_empty ())
-    {
-      gimple g = v.last ();
-      /* Remove statements for BBs that have been already processed.  */
-      sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
-      if (si->visited_p)
-	v.pop ();
-      else
-	break;
-    }
-
   unsigned int i;
   gimple g;
   gimple to_pop = NULL;
@@ -323,17 +323,9 @@ maybe_optimize_asan_check_ifn (struct sanopt_ctx *ctx, gimple stmt)
 	  continue;
 	}
 
-      if (TREE_CODE (len) != INTEGER_CST)
-	{
-	  /* If there is some stmts not followed by freeing call event
-	     for ptr already in the current bb, no need to insert anything.
-	     Non-constant len is treated just as length 1.  */
-	  if (gbb == bb)
-	    return false;
-	  break;
-	}
-
       tree glen = gimple_call_arg (g, 2);
+      gcc_assert (TREE_CODE (glen) == INTEGER_CST);
+
       /* If we've checked only smaller length than we want to check now,
 	 we can't remove the current stmt.  If g is in the same basic block,
 	 we want to remove it though, as the current stmt is better.  */
@@ -383,8 +375,70 @@ maybe_optimize_asan_check_ifn (struct sanopt_ctx *ctx, gimple stmt)
       v.truncate (j);
     }
 
+  return remove;
+}
+
+/* Optimize away redundant ASAN_CHECK calls.  */
+
+static bool
+maybe_optimize_asan_check_ifn (struct sanopt_ctx *ctx, gimple stmt)
+{
+  gcc_assert (gimple_call_num_args (stmt) == 4);
+  tree ptr = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
+  basic_block bb = gimple_bb (stmt);
+  sanopt_info *info = (sanopt_info *) bb->aux;
+
+  if (TREE_CODE (len) != INTEGER_CST)
+    return false;
+  if (integer_zerop (len))
+    return false;
+
+  gimple_set_uid (stmt, info->freeing_call_events);
+
+  auto_vec<gimple> *ptr_checks = &ctx->asan_check_map.get_or_insert (ptr);
+
+  tree base_addr = maybe_get_single_definition (ptr);
+  auto_vec<gimple> *base_checks = NULL;
+  if (base_addr)
+    {
+      base_checks = &ctx->asan_check_map.get_or_insert (base_addr);
+      /* Original pointer might have been invalidated.  */
+      ptr_checks = ctx->asan_check_map.get (ptr);
+    }
+
+  gimple g = maybe_get_dominating_check (*ptr_checks);
+
+  if (!g && base_checks)
+    /* Try with base address as well.  */
+    g = maybe_get_dominating_check (*base_checks);
+
+  if (!g)
+    {
+      /* For this PTR we don't have any ASAN_CHECK stmts recorded, so there's
+	 nothing to optimize yet.  */
+      ptr_checks->safe_push (stmt);
+      if (base_checks)
+	base_checks->safe_push (stmt);
+      return false;
+    }
+
+  bool remove = false;
+
+  if (ptr_checks)
+    remove = can_remove_asan_check (*ptr_checks, len, bb);
+
+  if (!remove && base_checks)
+    /* Try with base address as well.  */
+    remove = can_remove_asan_check (*base_checks, len, bb);
+
   if (!remove)
-    v.safe_push (stmt);
+    {
+      ptr_checks->safe_push (stmt);
+      if (base_checks)
+	base_checks->safe_push (stmt);
+    }
+
   return remove;
 }
 
@@ -404,10 +458,7 @@ sanopt_optimize_walker (basic_block bb, struct sanopt_ctx *ctx)
   basic_block son;
   gimple_stmt_iterator gsi;
   sanopt_info *info = (sanopt_info *) bb->aux;
-  bool asan_check_optimize
-    = (flag_sanitize & SANITIZE_ADDRESS)
-      && ((flag_sanitize & flag_sanitize_recover
-	   & SANITIZE_KERNEL_ADDRESS) == 0);
+  bool asan_check_optimize = (flag_sanitize & SANITIZE_ADDRESS) != 0;
 
   for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
     {
-- 
1.7.9.5


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCHv2] Enhance ASAN_CHECK optimization
  2014-12-03  9:04                       ` Yury Gribov
@ 2014-12-03  9:07                         ` Jakub Jelinek
  0 siblings, 0 replies; 49+ messages in thread
From: Jakub Jelinek @ 2014-12-03  9:07 UTC (permalink / raw)
  To: Yury Gribov; +Cc: Marek Polacek, GCC Patches, Konstantin Serebryany

On Wed, Dec 03, 2014 at 12:04:13PM +0300, Yury Gribov wrote:
> On 12/03/2014 11:36 AM, Jakub Jelinek wrote:
> >I thought I've agreed that we can optimize checks away for asan even when
> >recovering.  The patch is ok for trunk anyway, we can perhaps make it
> >tunable later, or define asan_can_optimize_checks to true unconditionally.
> 
> Snap, looks like my English failed me.

I've been against it in several mails, but then you mentioned we do it
anyway in asan.c already for the local optimization and I've changed my mind
afterwards.  Perhaps you've missed the last mail?

>  How about this one?  I only checked
> asan.exp but the change looks really trivial (drop asan_can_optimize_checks
> and change asan_check_optimize).

Ok, thanks.

> >From 3bca06f351ed9a7c34f74c30951a7b3cd4907e74 Mon Sep 17 00:00:00 2001
> From: Yury Gribov <y.gribov@samsung.com>
> Date: Tue, 25 Nov 2014 11:49:11 +0300
> Subject: [PATCH] 2014-12-03  Yury Gribov  <y.gribov@samsung.com>
> 
> gcc/
> 	* sanopt.c (maybe_get_single_definition): New function.
> 	(can_remove_asan_check): Ditto.
> 	(struct tree_map_traits): New struct.
> 	(struct sanopt_ctx): Use custom traits for asan_check_map.
> 	(maybe_get_dominating_check): New function.
> 	(maybe_optimize_ubsan_null_ifn): Move code to
> 	maybe_get_dominating_check.
> 	(maybe_optimize_asan_check_ifn): Move code and take non-SSA expressions
> 	into account when optimizing.
> 	(sanopt_optimize_walker): Optimize ASan checks even when
> 	recovering.

	Jakub

^ permalink raw reply	[flat|nested] 49+ messages in thread

end of thread, other threads:[~2014-12-03  9:07 UTC | newest]

Thread overview: 49+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-11-03 14:28 [PATCH] Optimize UBSAN_NULL checks, add sanopt.c Marek Polacek
2014-11-03 15:35 ` Jakub Jelinek
2014-11-04 18:36   ` Marek Polacek
2014-11-04 19:18     ` Jakub Jelinek
2014-11-05  9:19 ` Yury Gribov
2014-11-05  9:33   ` Jakub Jelinek
2014-11-05  9:54     ` Yury Gribov
2014-11-05 10:29       ` Marek Polacek
2014-11-05 10:50         ` Jakub Jelinek
2014-11-05 11:23           ` Marek Polacek
2014-11-05 12:16             ` Yury Gribov
2014-11-05 12:22               ` Jakub Jelinek
2014-11-05 12:34                 ` Yury Gribov
2014-11-05 13:13                   ` Yury Gribov
2014-11-05 13:23                     ` Jakub Jelinek
2014-11-05 13:48                       ` Yury Gribov
2014-11-12  9:52                         ` Maxim Ostapenko
2014-11-12 10:11                           ` Jakub Jelinek
2014-11-12 11:54                             ` Maxim Ostapenko
2014-11-12 22:53                               ` [PATCH] Propagate nonfreeing_call_p using ipa-pure-const Jakub Jelinek
2014-11-12 23:11                                 ` Jan Hubicka
2014-11-13  7:45                                   ` Jakub Jelinek
2014-11-13  8:44                                   ` [PATCH] Propagate nonfreeing_call_p using ipa-pure-const (take 2) Jakub Jelinek
2014-11-13 11:08                                     ` Richard Biener
2014-11-13 12:05                                     ` Jakub Jelinek
2014-11-14 17:44                                       ` Jakub Jelinek
2014-11-14 17:51                                         ` Jan Hubicka
2014-11-11 17:49           ` [RFC PATCH] Optimize ASAN_CHECK checks Jakub Jelinek
2014-11-12  9:26             ` Yury Gribov
2014-11-12 10:35               ` Jakub Jelinek
2014-11-12 11:12                 ` Yury Gribov
2014-11-12 22:41                   ` [PATCH] " Jakub Jelinek
2014-11-14 11:31                     ` Dodji Seketeli
2014-11-14 12:02                       ` Jakub Jelinek
2014-11-14 12:16                         ` Dodji Seketeli
2014-11-14 12:18                           ` Jakub Jelinek
2014-11-14 12:28                             ` Richard Biener
2014-11-14 13:06                               ` Jakub Jelinek
2014-11-14 17:30                           ` Jakub Jelinek
2014-11-25 17:26                 ` [PATCH] Enhance ASAN_CHECK optimization Yury Gribov
2014-11-26  9:59                   ` Jakub Jelinek
2014-11-26 18:43                     ` ygribov
2014-11-26 18:50                       ` Jakub Jelinek
2014-11-26 18:54                         ` ygribov
2014-11-26 19:00                           ` Jakub Jelinek
2014-12-03  8:09                   ` [PATCHv2] " Yury Gribov
2014-12-03  8:36                     ` Jakub Jelinek
2014-12-03  9:04                       ` Yury Gribov
2014-12-03  9:07                         ` Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).