Makefile.in (OBJS): Add sanopt.o.
authorMarek Polacek <polacek@redhat.com>
Tue, 4 Nov 2014 19:43:01 +0000 (19:43 +0000)
committerMarek Polacek <mpolacek@gcc.gnu.org>
Tue, 4 Nov 2014 19:43:01 +0000 (19:43 +0000)
* 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.

From-SVN: r217099

gcc/ChangeLog
gcc/Makefile.in
gcc/asan.c
gcc/asan.h
gcc/sanopt.c [new file with mode: 0644]
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/ubsan/align-2.c
gcc/testsuite/c-c++-common/ubsan/align-4.c
gcc/testsuite/g++.dg/ubsan/null-1.C
gcc/testsuite/g++.dg/ubsan/null-2.C

index 4f741cd4a3afa5a0ed5ae75a38b2c34236701487..7554f8093df42b2ec791437312cbfd368fc03bfe 100644 (file)
@@ -1,3 +1,12 @@
+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.
+
 2014-11-04  Jiong Wang  <jiong.wang@arm.com>
 2014-11-04  Wilco Dijkstra  <wilco.dijkstra@arm.com>
 
index 9c67fe21b7f241da580c4ff50142b21d42447433..f383032e9566f396ed7e87c9b153332a7a3b6874 100644 (file)
@@ -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@
index 8f146d2ce1dc128ef3a211b950a8270b02ad0fbd..79dede714181611f7cb4913c6a6c1563a644d401 100644 (file)
@@ -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"
index 8e3f0bae44cc092bcfd5dec938e783ad902ec7bf..f44839121182a6a6dc7f0dcafee01f90048a2805 100644 (file)
@@ -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 a/gcc/sanopt.c b/gcc/sanopt.c
new file mode 100644 (file)
index 0000000..f1d51d1
--- /dev/null
@@ -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 "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;
+
+                 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;
+                       }
+                   }
+
+                 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);
+}
index 104e597c209091f260ee7e29aa33d4d3a152ad50..03a368453c3913b4670fe05bb672bedc2fe4a8c1 100644 (file)
@@ -1,3 +1,10 @@
+2014-11-04  Marek Polacek  <polacek@redhat.com>
+
+       * 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.
+
 2014-11-04  Uros Bizjak  <ubizjak@gmail.com>
 
        * gcc.misc-tests/godump-1.c (dg-skip-if): Add alpha*-*-*.
index 071de8c202a782b7bf363edd196b9ce15ce3f9f3..02a26e2d301a04478cfc9c4f8223f2abdc4a8588 100644 (file)
@@ -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" } */
index 3252595d33014311345cd66362897bd584425d7f..f010589d18891b1410d2edfe83ac3e5e645a86cc 100644 (file)
@@ -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" } */
index e1524b1f922e277cf3065ed48738976fb5012513..83b3033b614900a49ab85d772f0e1292bc2ae336 100644 (file)
@@ -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)" }
index 88f387e17c917d274e6bc0dabef63b2e3e53a382..0230c7ca79c98553114934b96c70e39a919c0ff9 100644 (file)
@@ -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'" }