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 <[email protected]>
* 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