diff mbox

Support asan-instrumentation-with-call-threshold in GCC (second try)

Message ID 538D8136.3030402@samsung.com
State New
Headers show

Commit Message

Yury Gribov June 3, 2014, 8:03 a.m. UTC
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

Comments

Jakub Jelinek June 3, 2014, 8:37 a.m. UTC | #1
On Tue, Jun 03, 2014 at 12:03:02PM +0400, Yury Gribov wrote:
> --- 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 } };

Any reason why the BUILT_IN_* names so differ from the actual function
names?  I.e. why not use BUILT_IN_ASAN_{LOAD,STORE}{1,2,4,8,16,N}
(no underscore before N, no CHECK_)?

> @@ -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;

Wouldn't it be better to do
  bool use_calls
    = asan_num_accesses >= ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD;
(note, = on next line per GNU Coding Conventions), and then
  if (!use_calls)
    asan_num_accesses++;
so that you don't risk signed overflow?  Maybe also
  if (ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD == INT_MAX)
    use_calls = false;
before that, so that there is a way to turn the calls off completely.

> @@ -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);

This looks ugly.  I don't like adding GIMPLE_NOP, it is going
to take many passes before it is going to be DCEd.
Just handle the use_calls && before_p case in the two spots
where a new stmt is inserted.

Also note (but, already preexisting issue) is that the
__asan_report* and __asan_{load,store}* calls are declared in
sanitizer.def as having 1st argument PTR type, while in the library
it is uptr (aka PTRMODE).  So, either we should fix up sanitizer.def
to match that (we were passing already base_addr which is integral,
not pointer), or if we keep sanitizer.def as is, we should pass
to the calls base_ssa instead of 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)

Formatting, gimple_stmt_iterator needs to be below location_t.

Furthermore, this looks just like a partial transition, I don't see
why we should emit one __asan_loadN call but two separate
__asan_report_load_1 for the instrumented stringops.

Instead, build_check_stmt should be changed, so that it is possible to pass
it a variable length (checked by the caller for non-zero already),
and in that case force using the slow_p stuff, but instead of adding a
constant size add the variable length - 1.

> @@ -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;
> +

See above.

> @@ -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;
> +    }
> +

See above, here we should not handle use_calls any differently from
!use_calls.

	Jakub
Yury Gribov June 3, 2014, 9:33 a.m. UTC | #2
> Any reason why the BUILT_IN_* names so differ from the actual function
> names?  I.e. why not use BUILT_IN_ASAN_{LOAD,STORE}{1,2,4,8,16,N}
> (no underscore before N, no CHECK_)?

Makes sense.

> Wouldn't it be better to do
> ...
> so that you don't risk signed overflow?  Maybe also

Yeah, that looks cleaner.

> Also note (but, already preexisting issue) is that the
> __asan_report* and __asan_{load,store}* calls are declared in
> sanitizer.def as having 1st argument PTR type, while in the library
> it is uptr (aka PTRMODE).  So, either we should fix up sanitizer.def
> to match that (we were passing already base_addr which is integral,
> not pointer), or if we keep sanitizer.def as is, we should pass
> to the calls base_ssa instead of base_addr.

Which solution is better for middle-end? Would casting to uptr 
complicate alias analysis or somehow damage optimization capabilities of 
further passes?

Frankly I don't understand why libsanitizer prefers uptr to void*'s and 
size_t's...

> Furthermore, this looks just like a partial transition, I don't see
> why we should emit one __asan_loadN call but two separate
> __asan_report_load_1 for the instrumented stringops.

I was too lazy to bother with strlen. But ok, I'll optimize this piece.

> Instead, build_check_stmt should be changed, so that it is possible to pass
> it a variable length (checked by the caller for non-zero already),
> and in that case force using the slow_p stuff, but instead of adding a
> constant size add the variable length - 1.

+1, this will simplify code.

-Y
Konstantin Serebryany June 3, 2014, 9:37 a.m. UTC | #3
On Tue, Jun 3, 2014 at 1:33 PM, Yury Gribov <y.gribov@samsung.com> wrote:
>> Any reason why the BUILT_IN_* names so differ from the actual function
>> names?  I.e. why not use BUILT_IN_ASAN_{LOAD,STORE}{1,2,4,8,16,N}
>> (no underscore before N, no CHECK_)?
>
>
> Makes sense.
>
>> Wouldn't it be better to do
>> ...
>>
>> so that you don't risk signed overflow?  Maybe also
>
>
> Yeah, that looks cleaner.
>
>
>> Also note (but, already preexisting issue) is that the
>> __asan_report* and __asan_{load,store}* calls are declared in
>> sanitizer.def as having 1st argument PTR type, while in the library
>> it is uptr (aka PTRMODE).  So, either we should fix up sanitizer.def
>> to match that (we were passing already base_addr which is integral,
>> not pointer), or if we keep sanitizer.def as is, we should pass
>> to the calls base_ssa instead of base_addr.
>
>
> Which solution is better for middle-end? Would casting to uptr complicate
> alias analysis or somehow damage optimization capabilities of further
> passes?
>
> Frankly I don't understand why libsanitizer prefers uptr to void*'s and
> size_t's...

We can not use size_t because we do not include system headers.


>
>
>> Furthermore, this looks just like a partial transition, I don't see
>> why we should emit one __asan_loadN call but two separate
>> __asan_report_load_1 for the instrumented stringops.
>
>
> I was too lazy to bother with strlen. But ok, I'll optimize this piece.

Note that in clang we stopped instrumenting stringops in compiler and
fully rely on interceptors
({memset,memcpy,memmove} are replaced with
__asan_{memset,memcpy,memmove} to avoid late inlining)

>
>
>> Instead, build_check_stmt should be changed, so that it is possible to
>> pass
>> it a variable length (checked by the caller for non-zero already),
>> and in that case force using the slow_p stuff, but instead of adding a
>> constant size add the variable length - 1.
>
>
> +1, this will simplify code.
>
> -Y
Jakub Jelinek June 3, 2014, 9:37 a.m. UTC | #4
On Tue, Jun 03, 2014 at 01:33:39PM +0400, Yury Gribov wrote:
> >Also note (but, already preexisting issue) is that the
> >__asan_report* and __asan_{load,store}* calls are declared in
> >sanitizer.def as having 1st argument PTR type, while in the library
> >it is uptr (aka PTRMODE).  So, either we should fix up sanitizer.def
> >to match that (we were passing already base_addr which is integral,
> >not pointer), or if we keep sanitizer.def as is, we should pass
> >to the calls base_ssa instead of base_addr.
> 
> Which solution is better for middle-end? Would casting to uptr
> complicate alias analysis or somehow damage optimization
> capabilities of further passes?

Supposedly at least for use_calls case pretending the argument is
void * instead of uptr would result in smaller IL (no need to cast it
to integral type).

> Frankly I don't understand why libsanitizer prefers uptr to void*'s
> and size_t's...

Supposedly because then you don't have to cast it on the library side
if you want to perform masking/shifting etc. on it.  But that doesn't sound
like a very strong argument.

	Jakub
diff mbox

Patch

2014-06-03  Yury Gribov  <y.gribov@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 } } */
+