Hi all,
This is a second try on outline Asan instrumentation (original patch:
https://gcc.gnu.org/ml/gcc-patches/2014-05/msg02194.html).
This patch adds support for replacing Asan inline checks with function
calls. This feature has been recently added to LLVM to
* reduce long compiler runtimes on large functions with huge (over 10K)
number of memory accesses (http://llvm.org/bugs/show_bug.cgi?id=12653)
* a step to full Kasan support in GCC
* reduce code size overhead
Bootstrapped/regtested on x64.
-Y
2014-06-03 Yury Gribov <y.gri...@samsung.com>
New asan-instrumentation-with-call-threshold parameter.
gcc/
* asan.c (check_func): New function.
(build_check_stmt_sized): Likewise.
(build_check_stmt): Add support for new parameter.
(instrument_mem_region_access): Likewise.
(asan_instrument): Likewise.
* cfgcleanup.c (old_insns_match_p): Add support for new
functions.
* doc/invoke.texi: Describe new parameter.
* params.def: Define new parameter.
* params.h: Likewise.
* sanitizer.def: Describe new builtins.
gcc/testsuite/
* c-c++-common/asan/instrument-with-calls-1.c: New test.
* c-c++-common/asan/instrument-with-calls-2.c: Likewise.
* c-c++-common/asan/instrument-with-calls-3.c: Likewise.
diff --git a/gcc/asan.c b/gcc/asan.c
index 3397655..1677c51 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -257,6 +257,8 @@ struct asan_mem_ref
static alloc_pool asan_mem_ref_alloc_pool;
+static int asan_num_accesses;
+
/* This creates the alloc pool used to store the instances of
asan_mem_ref that are stored in the hash table asan_mem_ref_ht. */
@@ -1335,6 +1337,26 @@ report_error_func (bool is_store, HOST_WIDE_INT size_in_bytes, bool slow_p)
return builtin_decl_implicit (report[is_store][exact_log2 (size_in_bytes)]);
}
+/* Construct a function tree for __asan_{load,store}{1,2,4,8,16,_n}.
+ IS_STORE is either 1 (for a store) or 0 (for a load). */
+
+static tree
+check_func (bool is_store, int size_in_bytes, int slow_p)
+{
+ static enum built_in_function check[2][6]
+ = { { BUILT_IN_ASAN_CHECK_LOAD1, BUILT_IN_ASAN_CHECK_LOAD2,
+ BUILT_IN_ASAN_CHECK_LOAD4, BUILT_IN_ASAN_CHECK_LOAD8,
+ BUILT_IN_ASAN_CHECK_LOAD16, BUILT_IN_ASAN_CHECK_LOAD_N },
+ { BUILT_IN_ASAN_CHECK_STORE1, BUILT_IN_ASAN_CHECK_STORE2,
+ BUILT_IN_ASAN_CHECK_STORE4, BUILT_IN_ASAN_CHECK_STORE8,
+ BUILT_IN_ASAN_CHECK_STORE16, BUILT_IN_ASAN_CHECK_STORE_N } };
+ if ((size_in_bytes & (size_in_bytes - 1)) != 0
+ || size_in_bytes > 16
+ || slow_p)
+ return builtin_decl_implicit (check[is_store][5]);
+ return builtin_decl_implicit (check[is_store][exact_log2 (size_in_bytes)]);
+}
+
/* Split the current basic block and create a condition statement
insertion point right before or after the statement pointed to by
ITER. Return an iterator to the point at which the caller might
@@ -1520,9 +1542,11 @@ build_check_stmt (location_t location, tree base, gimple_stmt_iterator *iter,
tree shadow_type = TREE_TYPE (shadow_ptr_type);
tree uintptr_type
= build_nonstandard_integer_type (TYPE_PRECISION (TREE_TYPE (base)), 1);
- tree base_ssa = base;
+ tree base_ssa;
HOST_WIDE_INT real_size_in_bytes = size_in_bytes;
tree sz_arg = NULL_TREE;
+ bool use_calls =
+ asan_num_accesses++ >= ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD;
if (size_in_bytes == 1)
slow_p = false;
@@ -1534,15 +1558,27 @@ build_check_stmt (location_t location, tree base, gimple_stmt_iterator *iter,
slow_p = true;
}
- /* Get an iterator on the point where we can add the condition
- statement for the instrumentation. */
- gsi = create_cond_insert_point (iter, before_p,
- /*then_more_likely_p=*/false,
- /*create_then_fallthru_edge=*/false,
- &then_bb,
- &else_bb);
+ base_ssa = base = unshare_expr (base);
- base = unshare_expr (base);
+ if (use_calls)
+ {
+ gsi = *iter;
+ g = gimple_build_nop ();
+ if (before_p)
+ gsi_insert_before (&gsi, g, GSI_NEW_STMT);
+ else
+ gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+ }
+ else
+ {
+ /* Get an iterator on the point where we can add the condition
+ statement for the instrumentation. */
+ gsi = create_cond_insert_point (iter, before_p,
+ /*then_more_likely_p=*/false,
+ /*create_then_fallthru_edge=*/false,
+ &then_bb,
+ &else_bb);
+ }
/* BASE can already be an SSA_NAME; in that case, do not create a
new SSA_NAME for it. */
@@ -1563,6 +1599,22 @@ build_check_stmt (location_t location, tree base, gimple_stmt_iterator *iter,
gsi_insert_after (&gsi, g, GSI_NEW_STMT);
base_addr = gimple_assign_lhs (g);
+ if (use_calls)
+ {
+ tree fun = check_func (is_store, size_in_bytes, slow_p);
+ g = slow_p
+ ? gimple_build_call (fun, 1, base_addr)
+ : gimple_build_call (fun, 2, base_addr,
+ build_int_cst (pointer_sized_int_node, size_in_bytes));
+ gimple_set_location (g, location);
+ gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+
+ if (!before_p)
+ *iter = gsi;
+
+ return;
+ }
+
/* Build
(base_addr >> ASAN_SHADOW_SHIFT) + targetm.asan_shadow_offset (). */
shadow = build_shadow_mem_access (&gsi, location, base_addr,
@@ -1642,6 +1694,61 @@ build_check_stmt (location_t location, tree base, gimple_stmt_iterator *iter,
*iter = gsi_start_bb (else_bb);
}
+static void
+build_check_stmt_sized (location_t location, tree base, tree len,
+ gimple_stmt_iterator *iter, bool is_store)
+{
+ gimple g;
+ tree t;
+ gimple_stmt_iterator gsi = *iter;
+ tree uintptr_type
+ = build_nonstandard_integer_type (TYPE_PRECISION (TREE_TYPE (base)), 1);
+ tree base_ssa;
+ tree base_addr;
+
+ base_ssa = base = unshare_expr (base);
+
+ if (TREE_CODE (len) != SSA_NAME)
+ {
+ t = make_ssa_name (TREE_TYPE (len), NULL);
+ g = gimple_build_assign_with_ops (TREE_CODE (len), t, len, NULL);
+ gimple_set_location (g, location);
+ gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+ len = t;
+ }
+
+ if (!useless_type_conversion_p (size_type_node, TREE_TYPE (len)))
+ {
+ t = make_ssa_name (size_type_node, NULL);
+ g = gimple_build_assign_with_ops (NOP_EXPR, t, len, NULL);
+ gimple_set_location (g, location);
+ gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+ len = t;
+ }
+
+ if (TREE_CODE (base) != SSA_NAME)
+ {
+ g = gimple_build_assign_with_ops (TREE_CODE (base),
+ make_ssa_name (TREE_TYPE (base), NULL),
+ base, NULL_TREE);
+ gimple_set_location (g, location);
+ gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+ base_ssa = gimple_assign_lhs (g);
+ }
+
+ g = gimple_build_assign_with_ops (NOP_EXPR,
+ make_ssa_name (uintptr_type, NULL),
+ base_ssa, NULL_TREE);
+ gimple_set_location (g, location);
+ gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+ base_addr = gimple_assign_lhs (g);
+
+ g = gimple_build_call (check_func (is_store, 0, 1),
+ 2, base_addr, len);
+ gimple_set_location (g, location);
+ gsi_insert_before (&gsi, g, GSI_NEW_STMT);
+}
+
/* If T represents a memory access, add instrumentation code before ITER.
LOCATION is source code location.
IS_STORE is either TRUE (for a store) or FALSE (for a load). */
@@ -1774,6 +1881,9 @@ instrument_mem_region_access (tree base, tree len,
gimple_stmt_iterator *iter,
location_t location, bool is_store)
{
+ bool use_calls =
+ asan_num_accesses++ >= ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD;
+
if (!POINTER_TYPE_P (TREE_TYPE (base))
|| !INTEGRAL_TYPE_P (TREE_TYPE (len))
|| integer_zerop (len))
@@ -1795,6 +1905,14 @@ instrument_mem_region_access (tree base, tree len,
if (start_instrumented && end_instrumented)
return;
+ if (use_calls)
+ {
+ build_check_stmt_sized (location, base, len, iter, is_store);
+ update_mem_ref_hash_table (base, 1);
+ update_mem_ref_hash_table (end, 1);
+ return;
+ }
+
if (!is_gimple_constant (len))
{
/* So, the length of the memory area to asan-protect is
@@ -2568,6 +2686,7 @@ asan_instrument (void)
{
if (shadow_ptr_types[0] == NULL_TREE)
asan_init_shadow_ptr_types ();
+ asan_num_accesses = 0;
transform_statements ();
return 0;
}
diff --git a/gcc/cfgcleanup.c b/gcc/cfgcleanup.c
index 7c24a6d..cfdd059 100644
--- a/gcc/cfgcleanup.c
+++ b/gcc/cfgcleanup.c
@@ -1175,7 +1175,7 @@ old_insns_match_p (int mode ATTRIBUTE_UNUSED, rtx i1, rtx i2)
&& DECL_FUNCTION_CODE (SYMBOL_REF_DECL (symbol))
>= BUILT_IN_ASAN_REPORT_LOAD1
&& DECL_FUNCTION_CODE (SYMBOL_REF_DECL (symbol))
- <= BUILT_IN_ASAN_REPORT_STORE16)
+ <= BUILT_IN_ASAN_CHECK_STORE_N)
return dir_none;
}
}
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 9475594..96d6895 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -10230,6 +10230,12 @@ is enabled by default when using @option{-fsanitize=address} option.
To disable use-after-return detection use
@option{--param asan-use-after-return=0}.
+@item asan-instrumentation-with-call-threshold
+Once number of memory accesses in function becomes greater
+or equal than this number, use callbacks instead of
+generating inline code. E.g. to disable inline code use
+@option{--param asan-instrumentation-with-call-threshold=0}.
+
@end table
@end table
diff --git a/gcc/params.def b/gcc/params.def
index c3a8797..28ef79a 100644
--- a/gcc/params.def
+++ b/gcc/params.def
@@ -1090,6 +1090,12 @@ DEFPARAM (PARAM_ASAN_USE_AFTER_RETURN,
"Enable asan builtin functions protection",
1, 0, 1)
+DEFPARAM (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD,
+ "asan-instrumentation-with-call-threshold",
+ "Use callbacks instead of inline code once number of accesses "
+ " in function becomes greater or equal than this threshold",
+ 10000, 0, INT_MAX)
+
DEFPARAM (PARAM_UNINIT_CONTROL_DEP_ATTEMPTS,
"uninit-control-dep-attempts",
"Maximum number of nested calls to search for control dependencies "
diff --git a/gcc/params.h b/gcc/params.h
index 0d6daa2..d488e32 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -232,5 +232,7 @@ extern void init_param_values (int *params);
PARAM_VALUE (PARAM_ASAN_MEMINTRIN)
#define ASAN_USE_AFTER_RETURN \
PARAM_VALUE (PARAM_ASAN_USE_AFTER_RETURN)
+#define ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD \
+ PARAM_VALUE (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD)
#endif /* ! GCC_PARAMS_H */
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 4016fc5..5649c6d 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -29,7 +29,7 @@ along with GCC; see the file COPYING3. If not see
/* Address Sanitizer */
DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_INIT, "__asan_init_v3",
BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
-/* Do not reorder the BUILT_IN_ASAN_REPORT* builtins, e.g. cfgcleanup.c
+/* Do not reorder the BUILT_IN_ASAN_{REPORT,CHECK}* builtins, e.g. cfgcleanup.c
relies on this order. */
DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_LOAD1, "__asan_report_load1",
BT_FN_VOID_PTR, ATTR_TMPURE_NORETURN_NOTHROW_LEAF_LIST)
@@ -57,6 +57,30 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_STORE16, "__asan_report_store16",
DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_STORE_N, "__asan_report_store_n",
BT_FN_VOID_PTR_PTRMODE,
ATTR_TMPURE_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CHECK_LOAD1, "__asan_load1",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CHECK_LOAD2, "__asan_load2",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CHECK_LOAD4, "__asan_load4",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CHECK_LOAD8, "__asan_load8",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CHECK_LOAD16, "__asan_load16",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CHECK_LOAD_N, "__asan_loadN",
+ BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CHECK_STORE1, "__asan_store1",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CHECK_STORE2, "__asan_store2",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CHECK_STORE4, "__asan_store4",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CHECK_STORE8, "__asan_store8",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CHECK_STORE16, "__asan_store16",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CHECK_STORE_N, "__asan_storeN",
+ BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REGISTER_GLOBALS,
"__asan_register_globals",
BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
diff --git a/gcc/testsuite/c-c++-common/asan/instrument-with-calls-1.c b/gcc/testsuite/c-c++-common/asan/instrument-with-calls-1.c
new file mode 100644
index 0000000..80f7620
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/asan/instrument-with-calls-1.c
@@ -0,0 +1,10 @@
+/* { dg-do assemble } */
+/* { dg-options "--param asan-instrumentation-with-call-threshold=0 -save-temps" } */
+
+void f(char *a, int *b) {
+ *b = *a;
+}
+
+/* { dg-final { scan-assembler "__asan_load1" } } */
+/* { dg-final { scan-assembler "__asan_store4" } } */
+/* { dg-final { cleanup-saved-temps } } */
diff --git a/gcc/testsuite/c-c++-common/asan/instrument-with-calls-2.c b/gcc/testsuite/c-c++-common/asan/instrument-with-calls-2.c
new file mode 100644
index 0000000..570f796
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/asan/instrument-with-calls-2.c
@@ -0,0 +1,16 @@
+/* { dg-do assemble } */
+/* { dg-options "--param asan-instrumentation-with-call-threshold=1 -save-temps" } */
+
+int x;
+
+void f(int *a, int *b) {
+ *a = 0;
+ asm volatile ("" ::: "memory");
+ x = *b;
+}
+
+/* { dg-final { scan-assembler-not "__asan_store4" } } */
+/* { dg-final { scan-assembler "__asan_report_store4" } } */
+/* { dg-final { scan-assembler "__asan_load4" } } */
+/* { dg-final { scan-assembler-not "__asan_report_load4" } } */
+/* { dg-final { cleanup-saved-temps } } */
diff --git a/gcc/testsuite/c-c++-common/asan/instrument-with-calls-3.c b/gcc/testsuite/c-c++-common/asan/instrument-with-calls-3.c
new file mode 100644
index 0000000..3712c7a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/asan/instrument-with-calls-3.c
@@ -0,0 +1,15 @@
+/* { dg-do assemble } */
+/* { dg-options "--param asan-instrumentation-with-call-threshold=0 -save-temps" } */
+
+struct A {
+ char x[7];
+};
+
+void f(struct A *x, struct A *y) {
+ *x = *y;
+}
+
+/* { dg-final { scan-assembler "__asan_loadN" } } */
+/* { dg-final { scan-assembler "__asan_storeN" } } */
+/* { dg-final { cleanup-saved-temps } } */
+