This patch tries to optimize away redundant UBSAN_NULL checks.
It walks the statements, looks for UBSAN_NULL calls and keeps
track of pointers and statements checking that pointer in a
hash map.  Now, if we can prove that some UBSAN_NULL stmt is
dominated by other one which requires the same or less strict
alignment, there's no point in keeping this check around and
expanding it.

optimize_checks should be enhanced to handle other {ub,a,t}san
checks as well - which is what I'm going to work on next.

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

2014-10-30  Marek Polacek  <pola...@redhat.com>

        * asan.c (optimize_checks): New function.
        (pass_sanopt::execute): Call it.
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/asan.c gcc/asan.c
index b4b0822..c06bb49 100644
--- gcc/asan.c
+++ gcc/asan.c
@@ -2707,6 +2707,99 @@ asan_expand_check_ifn (gimple_stmt_iterator *iter, bool 
use_calls)
   return true;
 }
 
+/* Try to remove redundant sanitizer checks in function FUN.  */
+
+void
+optimize_checks (function *fun)
+{
+  basic_block bb;
+
+  /* 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 = NULL;
+  calculate_dominance_info (CDI_DOMINATORS);
+
+  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 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);
+
+                 if (null_check_map == NULL)
+                   null_check_map = new hash_map<tree, auto_vec<gimple> >;
+
+                 auto_vec<gimple> &v = 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, and is dominated by an earlier stmt.  */
+                     int i;
+                     gimple g;
+
+                     FOR_EACH_VEC_ELT (v, i, g)
+                       {
+                         tree align = gimple_call_arg (g, 2);
+                         basic_block gbb = gimple_bb (g);
+                         if (tree_int_cst_le (cur_align, align)
+                             && dominated_by_p (CDI_DOMINATORS, bb, gbb))
+                           {
+                             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);
+                   }
+                 break;
+               }
+             default:
+               break;
+             }
+
+         /* If we were able to remove current statement, gsi_remove
+            already pointed us to the next statement.  */
+         if (!remove)
+           gsi_next (&gsi);
+       }
+    }
+
+  delete null_check_map;
+  null_check_map = NULL;
+  free_dominance_info (CDI_DOMINATORS);
+}
+
 /* Instrument the current function.  */
 
 static unsigned int
@@ -2834,6 +2927,10 @@ pass_sanopt::execute (function *fun)
 {
   basic_block bb;
 
+  /* Try to remove redundant checks.  */
+  if (optimize && (flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT)))
+    optimize_checks (fun);
+
   int asan_num_accesses = 0;
   if (flag_sanitize & SANITIZE_ADDRESS)
     {
@@ -2889,7 +2986,7 @@ pass_sanopt::execute (function *fun)
 
          if (dump_file && (dump_flags & TDF_DETAILS))
            {
-             fprintf (dump_file, "Optimized\n  ");
+             fprintf (dump_file, "Expanded\n  ");
              print_gimple_stmt (dump_file, stmt, 0, dump_flags);
              fprintf (dump_file, "\n");
            }
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

Reply via email to