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