Build_check_stmt is now unified for const/variable lengths
but I'd still prefer to treat the use_calls case specially
because calling single __asan_loadN is more space-efficient
than emitting two separate calls with a length check.

That is not what I meant.
1) instrument_mem_region_access/instrument_strlen should use a single
   build_check_stmt call instead of two that they do now,
   both for use_calls case and !use_calls case, the difference
   is just that instrument_mem_region_access needs to guard
   it against len == 0, while for use_calls it doesn't have to,
   it seems __asan_{load,store}N handles length of 0 correctly
   (still, the caller shouldn't update_mem_ref_hash_table
   if length might be zero, even in the use_calls case)
2) passing always a tree length to build_check_stmt and then
   recreating size_in_bytes out of it is ugly, just add a new
   tree parameter, if it is non-NULL, the length is variable,
   and real_size_in_bytes should be 1, and the code additionally
   has to compute length - 1 at runtime and add the result,
   otherwise it just keeps adding precomputed constant

Let's check if I'm on the right way. I've attached current version. It may have some rough edges but at least all regtests pass on x64.

Some obvious problems:

1) build_check_stmt argument list has become really long and messy;
(this could be simplified to some extent if we get rid of size_of_bytes and non_zero_len_p and recompute them from len instead
but all other parameters seem really unavoidable)

2) we can't easily check number of instrumentations in no-redundant-instrumentation-*.c family of tests because memory region accesses now have single error handler (__asan_report_load_n/store_n);
the best solution I came up with is counting all 'X & 7' patterns.

3) strlen instrumentation is still using two separate calls to build_check_stmt (due to reasons I mentioned above).

-Y
diff --git a/gcc/asan.c b/gcc/asan.c
index 3397655..fb1ddb9 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -242,6 +242,19 @@ static GTY(()) tree shadow_ptr_types[2];
 /* Decl for __asan_option_detect_stack_use_after_return.  */
 static GTY(()) tree asan_detect_stack_use_after_return;
 
+/* Number of instrumentations in current function so far.  */
+
+static int asan_num_accesses;
+
+/* Check whether we should replace inline instrumentation with calls.  */
+
+static inline bool
+use_calls_p ()
+{
+  return ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD < INT_MAX
+    && asan_num_accesses >= ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD;
+}
+
 /* Hashtable support for memory references used by gimple
    statements.  */
 
@@ -1319,7 +1332,7 @@ asan_protect_global (tree decl)
    IS_STORE is either 1 (for a store) or 0 (for a load).  */
 
 static tree
-report_error_func (bool is_store, HOST_WIDE_INT size_in_bytes, bool slow_p)
+report_error_func (bool is_store, HOST_WIDE_INT size_in_bytes, int *nargs)
 {
   static enum built_in_function report[2][6]
     = { { BUILT_IN_ASAN_REPORT_LOAD1, BUILT_IN_ASAN_REPORT_LOAD2,
@@ -1328,13 +1341,37 @@ report_error_func (bool is_store, HOST_WIDE_INT size_in_bytes, bool slow_p)
 	{ BUILT_IN_ASAN_REPORT_STORE1, BUILT_IN_ASAN_REPORT_STORE2,
 	  BUILT_IN_ASAN_REPORT_STORE4, BUILT_IN_ASAN_REPORT_STORE8,
 	  BUILT_IN_ASAN_REPORT_STORE16, BUILT_IN_ASAN_REPORT_STORE_N } };
-  if ((size_in_bytes & (size_in_bytes - 1)) != 0
-      || size_in_bytes > 16
-      || slow_p)
-    return builtin_decl_implicit (report[is_store][5]);
+  if (size_in_bytes == -1)
+    {
+      *nargs = 2;
+      return builtin_decl_implicit (report[is_store][5]);
+    }
+  *nargs = 1;
   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 *nargs)
+{
+  static enum built_in_function check[2][6]
+    = { { BUILT_IN_ASAN_LOAD1, BUILT_IN_ASAN_LOAD2,
+      BUILT_IN_ASAN_LOAD4, BUILT_IN_ASAN_LOAD8,
+      BUILT_IN_ASAN_LOAD16, BUILT_IN_ASAN_LOADN },
+	{ BUILT_IN_ASAN_STORE1, BUILT_IN_ASAN_STORE2,
+      BUILT_IN_ASAN_STORE4, BUILT_IN_ASAN_STORE8,
+      BUILT_IN_ASAN_STORE16, BUILT_IN_ASAN_STOREN } };
+  if (size_in_bytes == -1)
+    {
+      *nargs = 2;
+      return builtin_decl_implicit (check[is_store][5]);
+    }
+  *nargs = 1;
+  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
@@ -1494,6 +1531,76 @@ build_shadow_mem_access (gimple_stmt_iterator *gsi, location_t location,
   return gimple_assign_lhs (g);
 }
 
+/* BASE can already be an SSA_NAME; in that case, do not create a
+   new SSA_NAME for it.  */
+
+static tree
+maybe_create_ssa_name (location_t loc, tree base, gimple_stmt_iterator *iter,
+                       bool before_p)
+{
+  if (TREE_CODE (base) == SSA_NAME)
+    return base;
+  gimple g
+    = gimple_build_assign_with_ops (TREE_CODE (base),
+				    make_ssa_name (TREE_TYPE (base), NULL),
+				    base, NULL_TREE);
+  gimple_set_location (g, loc);
+  if (before_p)
+    gsi_insert_before (iter, g, GSI_SAME_STMT);
+  else
+    gsi_insert_after (iter, g, GSI_NEW_STMT);
+  return gimple_assign_lhs (g);
+}
+
+/* Instrument the memory access instruction using callbacks.
+   Parameters are similar to BUILD_CHECK_STMT.  */
+
+static void
+build_check_stmt_with_calls (location_t loc, tree base, tree len,
+                  HOST_WIDE_INT size_in_bytes, gimple_stmt_iterator *iter,
+                  bool before_p, bool is_store, bool is_scalar_access)
+{
+  gimple_stmt_iterator gsi = *iter;
+  tree base_ssa = maybe_create_ssa_name (loc, base, &gsi, before_p);
+
+  gimple g
+    = gimple_build_assign_with_ops (NOP_EXPR,
+				    make_ssa_name (pointer_sized_int_node, NULL),
+				    base_ssa, NULL_TREE);
+  gimple_set_location (g, loc);
+  if (before_p)
+    gsi_insert_before (&gsi, g, GSI_NEW_STMT);
+  else
+    gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+  tree base_addr = gimple_assign_lhs (g);
+
+  int nargs;
+  tree fun
+    = check_func (is_store, is_scalar_access ? size_in_bytes : -1, &nargs);
+  if (nargs == 1)
+    g = gimple_build_call (fun, 1, base_addr);
+  else
+    {
+      gcc_assert (nargs == 2);
+      g = gimple_build_assign_with_ops (NOP_EXPR,
+				        make_ssa_name (pointer_sized_int_node,
+                                                       NULL),
+				        len, NULL_TREE);
+      gimple_set_location (g, loc);
+      gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+      tree sz_arg = gimple_assign_lhs (g);
+      g = gimple_build_call (fun, nargs, base_addr, sz_arg);
+    }
+  gimple_set_location (g, loc);
+  gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+
+  if (!before_p)
+    {
+      gsi_next (&gsi);
+      *iter = gsi;
+    }
+}
+
 /* Instrument the memory access instruction BASE.  Insert new
    statements before or after ITER.
 
@@ -1501,111 +1608,198 @@ build_shadow_mem_access (gimple_stmt_iterator *gsi, location_t location,
    SSA_NAME, or a non-SSA expression.  LOCATION is the source code
    location.  IS_STORE is TRUE for a store, FALSE for a load.
    BEFORE_P is TRUE for inserting the instrumentation code before
-   ITER, FALSE for inserting it after ITER.
+   ITER, FALSE for inserting it after ITER. IS_SCALAR_ACCESS is TRUE
+   for a scalar memory access and FALSE for memory region access.
+   NON_ZERO_P is TRUE if memory region is guaranteed to have non-zero
+   length. ALIGN tells alignment of accessed memory object.
+
+   START_INSTRUMENTED and END_INSTRUMENTED are TRUE if start/end of
+   memory region have already been instrumented.
 
    If BEFORE_P is TRUE, *ITER is arranged to still point to the
    statement it was pointing to prior to calling this function,
    otherwise, it points to the statement logically following it.  */
 
 static void
-build_check_stmt (location_t location, tree base, gimple_stmt_iterator *iter,
-		  bool before_p, bool is_store, HOST_WIDE_INT size_in_bytes,
-		  bool slow_p = false)
+build_check_stmt (location_t location, tree base, tree len,
+                  HOST_WIDE_INT size_in_bytes, gimple_stmt_iterator *iter,
+                  bool non_zero_len_p, bool before_p, bool is_store,
+                  bool is_scalar_access, unsigned int align = 0,
+                  bool start_instrumented = false,
+                  bool end_instrumented = false)
 {
-  gimple_stmt_iterator gsi;
-  basic_block then_bb, else_bb;
-  tree t, base_addr, shadow;
+  gimple_stmt_iterator gsi = *iter;
   gimple g;
-  tree shadow_ptr_type = shadow_ptr_types[size_in_bytes == 16 ? 1 : 0];
-  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;
-  HOST_WIDE_INT real_size_in_bytes = size_in_bytes;
-  tree sz_arg = NULL_TREE;
-
-  if (size_in_bytes == 1)
-    slow_p = false;
-  else if ((size_in_bytes & (size_in_bytes - 1)) != 0
-	   || size_in_bytes > 16
-	   || slow_p)
+
+  gcc_assert (!(size_in_bytes > 0 && !non_zero_len_p));
+
+  if (len)
+    {
+      len = unshare_expr (len);
+    }
+  else
     {
-      real_size_in_bytes = 1;
-      slow_p = true;
+      gcc_assert (size_in_bytes != -1);
+      len = build_int_cst (pointer_sized_int_node, size_in_bytes);
+    }
+
+  if (size_in_bytes > 1)
+    {
+      if ((size_in_bytes & (size_in_bytes - 1)) != 0
+	  || size_in_bytes > 16)
+	{
+	  size_in_bytes = -1;
+	}
+      else if (align && align < size_in_bytes * BITS_PER_UNIT)
+	{
+	  /* On non-strict alignment targets, if
+	     16-byte access is just 8-byte aligned,
+	     this will result in misaligned shadow
+	     memory 2 byte load, but otherwise can
+             be handled using one read.  */
+	  if (size_in_bytes != 16
+	      || STRICT_ALIGNMENT
+	      || align < 8 * BITS_PER_UNIT)
+	    {
+	      size_in_bytes = -1;
+	    }
+        }
+    }
+
+  HOST_WIDE_INT real_size_in_bytes = size_in_bytes == -1 ? 1 : size_in_bytes;
+
+  tree shadow_ptr_type = shadow_ptr_types[real_size_in_bytes == 16 ? 1 : 0];
+  tree shadow_type = TREE_TYPE (shadow_ptr_type);
+
+  base = unshare_expr (base);
+
+  if (use_calls_p ())
+    {
+      gsi = *iter;
+      build_check_stmt_with_calls (location, base, len, size_in_bytes, iter,
+                              before_p, is_store, is_scalar_access);
+      return;
+    }
+
+  ++asan_num_accesses;
+
+  if (!non_zero_len_p)
+    {
+      gcc_assert (before_p);
+
+      /* So, the length of the memory area to asan-protect is
+         non-constant.  Let's guard the generated instrumentation code
+	 like:
+
+	 if (len != 0)
+	   {
+	     //asan instrumentation code goes here.
+	   }
+	 // falltrough instructions, starting with *ITER.  */
+
+      g = gimple_build_cond (NE_EXPR,
+			     len,
+			     build_int_cst (TREE_TYPE (len), 0),
+			     NULL_TREE, NULL_TREE);
+      gimple_set_location (g, location);
+
+      basic_block then_bb, fallthrough_bb;
+      insert_if_then_before_iter (g, iter, /*then_more_likely_p=*/true,
+				  &then_bb, &fallthrough_bb);
+      /* Note that fallthrough_bb starts with the statement that was
+         pointed to by ITER.  */
+
+      /* The 'then block' of the 'if (len != 0) condition is where
+	 we'll generate the asan instrumentation code now.  */
+      gsi = gsi_last_bb (then_bb);
+      build_check_stmt (location, base, len, size_in_bytes, &gsi,
+                        /*non_zero_len_p*/true, /*before_p*/true, is_store,
+                        is_scalar_access, align,
+                        start_instrumented, end_instrumented);
+      return;
     }
 
   /* Get an iterator on the point where we can add the condition
      statement for the instrumentation.  */
-  gsi = create_cond_insert_point (iter, before_p,
+  basic_block then_bb, else_bb;
+  gsi = create_cond_insert_point (&gsi, before_p,
 				  /*then_more_likely_p=*/false,
 				  /*create_then_fallthru_edge=*/false,
 				  &then_bb,
 				  &else_bb);
 
-  base = unshare_expr (base);
-
-  /* BASE can already be an SSA_NAME; in that case, do not create a
-     new SSA_NAME for it.  */
-  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_after (&gsi, g, GSI_NEW_STMT);
-      base_ssa = gimple_assign_lhs (g);
-    }
+  tree base_ssa = maybe_create_ssa_name (location, base, &gsi,
+                                         /*before_p*/false);
 
   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_after (&gsi, g, GSI_NEW_STMT);
-  base_addr = gimple_assign_lhs (g);
-
-  /* Build
-     (base_addr >> ASAN_SHADOW_SHIFT) + targetm.asan_shadow_offset ().  */
-  shadow = build_shadow_mem_access (&gsi, location, base_addr,
-				    shadow_ptr_type);
+  tree base_addr = gimple_assign_lhs (g);
 
-  if (real_size_in_bytes < 8)
+  tree t;
+  if (real_size_in_bytes >= 8)
     {
-      /* Slow path for 1, 2 and 4 byte accesses.
-	 Test (shadow != 0)
-	      & ((base_addr & 7) + (real_size_in_bytes - 1)) >= shadow).  */
-      gimple_seq seq = NULL;
-      gimple shadow_test = build_assign (NE_EXPR, shadow, 0);
-      gimple_seq_add_stmt (&seq, shadow_test);
-      gimple_seq_add_stmt (&seq, build_assign (BIT_AND_EXPR, base_addr, 7));
-      gimple_seq_add_stmt (&seq, build_type_cast (shadow_type,
-                                                  gimple_seq_last (seq)));
-      if (real_size_in_bytes > 1)
-        gimple_seq_add_stmt (&seq,
-                             build_assign (PLUS_EXPR, gimple_seq_last (seq),
-					   real_size_in_bytes - 1));
-      gimple_seq_add_stmt (&seq, build_assign (GE_EXPR, gimple_seq_last (seq),
-                                               shadow));
-      gimple_seq_add_stmt (&seq, build_assign (BIT_AND_EXPR, shadow_test,
-                                               gimple_seq_last (seq)));
-      t = gimple_assign_lhs (gimple_seq_last (seq));
-      gimple_seq_set_location (seq, location);
-      gsi_insert_seq_after (&gsi, seq, GSI_CONTINUE_LINKING);
-      /* For weird access sizes or misaligned, check first and last byte.  */
-      if (slow_p)
+      tree shadow = build_shadow_mem_access (&gsi, location, base_addr,
+				             shadow_ptr_type);
+      t = shadow;
+    }
+  else
+    {
+      /* Slow path for 1, 2 and 4 byte accesses.  */
+
+      if (!start_instrumented)
+        {
+          /* Test (shadow != 0)
+	          & ((base_addr & 7) + (real_size_in_bytes - 1)) >= shadow).  */
+          tree shadow = build_shadow_mem_access (&gsi, location, base_addr,
+				                 shadow_ptr_type);
+          gimple shadow_test = build_assign (NE_EXPR, shadow, 0);
+          gimple_seq seq = NULL;
+          gimple_seq_add_stmt (&seq, shadow_test);
+          gimple_seq_add_stmt (&seq, build_assign (BIT_AND_EXPR, base_addr, 7));
+          gimple_seq_add_stmt (&seq, build_type_cast (shadow_type,
+                                                      gimple_seq_last (seq)));
+          if (real_size_in_bytes > 1)
+            gimple_seq_add_stmt (&seq,
+                                 build_assign (PLUS_EXPR, gimple_seq_last (seq),
+					       real_size_in_bytes - 1));
+          gimple_seq_add_stmt (&seq, build_assign (GE_EXPR,
+                                                   gimple_seq_last (seq),
+                                                   shadow));
+          gimple_seq_add_stmt (&seq, build_assign (BIT_AND_EXPR, shadow_test,
+                                                   gimple_seq_last (seq)));
+          t = gimple_assign_lhs (gimple_seq_last (seq));
+          gimple_seq_set_location (seq, location);
+          gsi_insert_seq_after (&gsi, seq, GSI_CONTINUE_LINKING);
+      }
+
+      /* For non-constant, misaligned or otherwise weird access sizes,
+         check first and last byte.  */
+      if (size_in_bytes == -1 && !end_instrumented)
 	{
+	  g = gimple_build_assign_with_ops (MINUS_EXPR,
+					    make_ssa_name (uintptr_type, NULL),
+					    len,
+					    build_int_cst (uintptr_type, 1));
+	  gimple_set_location (g, location);
+	  gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+	  tree last = gimple_assign_lhs (g);
 	  g = gimple_build_assign_with_ops (PLUS_EXPR,
 					    make_ssa_name (uintptr_type, NULL),
 					    base_addr,
-					    build_int_cst (uintptr_type,
-							   size_in_bytes - 1));
+					    last);
 	  gimple_set_location (g, location);
 	  gsi_insert_after (&gsi, g, GSI_NEW_STMT);
 	  tree base_end_addr = gimple_assign_lhs (g);
 
-	  shadow = build_shadow_mem_access (&gsi, location, base_end_addr,
+	  tree shadow = build_shadow_mem_access (&gsi, location, base_end_addr,
 					    shadow_ptr_type);
-	  seq = NULL;
-	  shadow_test = build_assign (NE_EXPR, shadow, 0);
+	  gimple shadow_test = build_assign (NE_EXPR, shadow, 0);
+	  gimple_seq seq = NULL;
 	  gimple_seq_add_stmt (&seq, shadow_test);
 	  gimple_seq_add_stmt (&seq, build_assign (BIT_AND_EXPR,
 						   base_end_addr, 7));
@@ -1616,16 +1810,14 @@ build_check_stmt (location_t location, tree base, gimple_stmt_iterator *iter,
 						   shadow));
 	  gimple_seq_add_stmt (&seq, build_assign (BIT_AND_EXPR, shadow_test,
 						   gimple_seq_last (seq)));
-	  gimple_seq_add_stmt (&seq, build_assign (BIT_IOR_EXPR, t,
-						   gimple_seq_last (seq)));
+          if (!start_instrumented)
+	    gimple_seq_add_stmt (&seq, build_assign (BIT_IOR_EXPR, t,
+                                                     gimple_seq_last (seq)));
 	  t = gimple_assign_lhs (gimple_seq_last (seq));
 	  gimple_seq_set_location (seq, location);
 	  gsi_insert_seq_after (&gsi, seq, GSI_CONTINUE_LINKING);
-	  sz_arg = build_int_cst (pointer_sized_int_node, size_in_bytes);
 	}
     }
-  else
-    t = shadow;
 
   g = gimple_build_cond (NE_EXPR, t, build_int_cst (TREE_TYPE (t), 0),
 			 NULL_TREE, NULL_TREE);
@@ -1634,8 +1826,23 @@ build_check_stmt (location_t location, tree base, gimple_stmt_iterator *iter,
 
   /* Generate call to the run-time library (e.g. __asan_report_load8).  */
   gsi = gsi_start_bb (then_bb);
-  g = gimple_build_call (report_error_func (is_store, size_in_bytes, slow_p),
-			 sz_arg ? 2 : 1, base_addr, sz_arg);
+  int nargs;
+  tree fun = report_error_func (is_store, is_scalar_access ? size_in_bytes : -1,
+                                &nargs);
+  if (nargs == 1)
+      g = gimple_build_call (fun, 1, base_addr);
+  else
+    {
+      gcc_assert (nargs == 2);
+      g = gimple_build_assign_with_ops (NOP_EXPR,
+				        make_ssa_name (pointer_sized_int_node,
+                                                       NULL),
+				        len, NULL_TREE);
+      gimple_set_location (g, location);
+      gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+      tree sz_arg = gimple_assign_lhs (g);
+      g = gimple_build_call (fun, nargs, base_addr, sz_arg);
+    }
   gimple_set_location (g, location);
   gsi_insert_after (&gsi, g, GSI_NEW_STMT);
 
@@ -1730,31 +1937,10 @@ instrument_derefs (gimple_stmt_iterator *iter, tree t,
   base = build_fold_addr_expr (t);
   if (!has_mem_ref_been_instrumented (base, size_in_bytes))
     {
-      bool slow_p = false;
-      if (size_in_bytes > 1)
-	{
-	  if ((size_in_bytes & (size_in_bytes - 1)) != 0
-	      || size_in_bytes > 16)
-	    slow_p = true;
-	  else
-	    {
-	      unsigned int align = get_object_alignment (t);
-	      if (align < size_in_bytes * BITS_PER_UNIT)
-		{
-		  /* On non-strict alignment targets, if
-		     16-byte access is just 8-byte aligned,
-		     this will result in misaligned shadow
-		     memory 2 byte load, but otherwise can
-		     be handled using one read.  */
-		  if (size_in_bytes != 16
-		      || STRICT_ALIGNMENT
-		      || align < 8 * BITS_PER_UNIT)
-		    slow_p = true;
-		}
-	    }
-	}
-      build_check_stmt (location, base, iter, /*before_p=*/true,
-			is_store, size_in_bytes, slow_p);
+      unsigned int align = get_object_alignment (t);
+      build_check_stmt (location, base, NULL_TREE, size_in_bytes, iter,
+                        /*non_zero_len_p*/size_in_bytes > 0, /*before_p=*/true,
+                        is_store, /*is_scalar_access*/true, align);
       update_mem_ref_hash_table (base, size_in_bytes);
       update_mem_ref_hash_table (t, size_in_bytes);
     }
@@ -1779,10 +1965,6 @@ instrument_mem_region_access (tree base, tree len,
       || integer_zerop (len))
     return;
 
-  gimple_stmt_iterator gsi = *iter;
-
-  basic_block fallthrough_bb = NULL, then_bb = NULL;
-
   /* If the beginning of the memory region has already been
      instrumented, do not instrument it.  */
   bool start_instrumented = has_mem_ref_been_instrumented (base, 1);
@@ -1792,130 +1974,15 @@ instrument_mem_region_access (tree base, tree len,
   tree end = asan_mem_ref_get_end (base, len);
   bool end_instrumented = has_mem_ref_been_instrumented (end, 1);
 
-  if (start_instrumented && end_instrumented)
-    return;
+  HOST_WIDE_INT size_in_bytes = tree_fits_shwi_p (len) ? tree_to_shwi (len) : -1;
 
-  if (!is_gimple_constant (len))
-    {
-      /* So, the length of the memory area to asan-protect is
-	 non-constant.  Let's guard the generated instrumentation code
-	 like:
+  build_check_stmt (location, base, len, size_in_bytes, iter,
+                    /*non_zero_len_p*/size_in_bytes > 0, /*before_p*/true,
+                    is_store, /*is_scalar_access*/false, /*align*/0,
+                    start_instrumented, end_instrumented);
 
-	 if (len != 0)
-	   {
-	     //asan instrumentation code goes here.
-	   }
-	   // falltrough instructions, starting with *ITER.  */
-
-      gimple g = gimple_build_cond (NE_EXPR,
-				    len,
-				    build_int_cst (TREE_TYPE (len), 0),
-				    NULL_TREE, NULL_TREE);
-      gimple_set_location (g, location);
-      insert_if_then_before_iter (g, iter, /*then_more_likely_p=*/true,
-				  &then_bb, &fallthrough_bb);
-      /* Note that fallthrough_bb starts with the statement that was
-	 pointed to by ITER.  */
-
-      /* The 'then block' of the 'if (len != 0) condition is where
-	 we'll generate the asan instrumentation code now.  */
-      gsi = gsi_last_bb (then_bb);
-    }
-
-  if (!start_instrumented)
-    {
-      /* Instrument the beginning of the memory region to be accessed,
-	 and arrange for the rest of the intrumentation code to be
-	 inserted in the then block *after* the current gsi.  */
-      build_check_stmt (location, base, &gsi, /*before_p=*/true, is_store, 1);
-
-      if (then_bb)
-	/* We are in the case where the length of the region is not
-	   constant; so instrumentation code is being generated in the
-	   'then block' of the 'if (len != 0) condition.  Let's arrange
-	   for the subsequent instrumentation statements to go in the
-	   'then block'.  */
-	gsi = gsi_last_bb (then_bb);
-      else
-        {
-          *iter = gsi;
-	  /* Don't remember this access as instrumented, if length
-	     is unknown.  It might be zero and not being actually
-	     instrumented, so we can't rely on it being instrumented.  */
-          update_mem_ref_hash_table (base, 1);
-	}
-    }
-
-  if (end_instrumented)
-    return;
-
-  /* We want to instrument the access at the end of the memory region,
-     which is at (base + len - 1).  */
-
-  /* offset = len - 1;  */
-  len = unshare_expr (len);
-  tree offset;
-  gimple_seq seq = NULL;
-  if (TREE_CODE (len) == INTEGER_CST)
-    offset = fold_build2 (MINUS_EXPR, size_type_node,
-			  fold_convert (size_type_node, len),
-			  build_int_cst (size_type_node, 1));
-  else
-    {
-      gimple g;
-      tree t;
-
-      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);
-	  gimple_seq_add_stmt_without_update (&seq, g);
-	  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);
-	  gimple_seq_add_stmt_without_update (&seq, g);
-	  len = t;
-	}
-
-      t = make_ssa_name (size_type_node, NULL);
-      g = gimple_build_assign_with_ops (MINUS_EXPR, t, len,
-					build_int_cst (size_type_node, 1));
-      gimple_set_location (g, location);
-      gimple_seq_add_stmt_without_update (&seq, g);
-      offset = gimple_assign_lhs (g);
-    }
-
-  /* _1 = base;  */
-  base = unshare_expr (base);
-  gimple region_end =
-    gimple_build_assign_with_ops (TREE_CODE (base),
-				  make_ssa_name (TREE_TYPE (base), NULL),
-				  base, NULL);
-  gimple_set_location (region_end, location);
-  gimple_seq_add_stmt_without_update (&seq, region_end);
-
-  /* _2 = _1 + offset;  */
-  region_end =
-    gimple_build_assign_with_ops (POINTER_PLUS_EXPR,
-				  make_ssa_name (TREE_TYPE (base), NULL),
-				  gimple_assign_lhs (region_end),
-				  offset);
-  gimple_set_location (region_end, location);
-  gimple_seq_add_stmt_without_update (&seq, region_end);
-  gsi_insert_seq_before (&gsi, seq, GSI_SAME_STMT);
-
-  /* instrument access at _2;  */
-  gsi = gsi_for_stmt (region_end);
-  build_check_stmt (location, gimple_assign_lhs (region_end),
-		    &gsi, /*before_p=*/false, is_store, 1);
-
-  if (then_bb == NULL)
-    update_mem_ref_hash_table (end, 1);
+  update_mem_ref_hash_table (base, 1);
+  update_mem_ref_hash_table (end, 1);
 
   *iter = gsi_for_stmt (gsi_stmt (*iter));
 }
@@ -1956,47 +2023,30 @@ instrument_strlen_call (gimple_stmt_iterator *iter)
   location_t loc = gimple_location (call);
   tree str_arg = gimple_call_arg (call, 0);
 
-  /* Instrument the access to the first byte of str_arg.  i.e:
-
-     _1 = str_arg; instrument (_1); */
   tree cptr_type = build_pointer_type (char_type_node);
   gimple str_arg_ssa =
     gimple_build_assign_with_ops (NOP_EXPR,
-				  make_ssa_name (cptr_type, NULL),
-				  str_arg, NULL);
+                                  make_ssa_name (cptr_type, NULL),
+                                  str_arg, NULL);
   gimple_set_location (str_arg_ssa, loc);
-  gimple_stmt_iterator gsi = *iter;
-  gsi_insert_before (&gsi, str_arg_ssa, GSI_NEW_STMT);
-  build_check_stmt (loc, gimple_assign_lhs (str_arg_ssa), &gsi,
-		    /*before_p=*/false, /*is_store=*/false, 1);
-
-  /* If we initially had an instruction like:
-
-	 int n = strlen (str)
+  gsi_insert_before (iter, str_arg_ssa, GSI_SAME_STMT);
 
-     we now want to instrument the access to str[n], after the
-     instruction above.*/
+  build_check_stmt (loc, gimple_assign_lhs (str_arg_ssa), NULL_TREE, 1, iter,
+                    /*non_zero_len_p*/true, /*before_p=*/true,
+                    /*is_store=*/false, /*is_scalar_access*/false, /*align*/0);
 
-  /* So let's build the access to str[n] that is, access through the
-     pointer_plus expr: (_1 + len).  */
   gimple stmt =
-    gimple_build_assign_with_ops (POINTER_PLUS_EXPR,
-				  make_ssa_name (cptr_type, NULL),
-				  gimple_assign_lhs (str_arg_ssa),
-				  len);
+    gimple_build_assign_with_ops (PLUS_EXPR,
+                                  make_ssa_name (TREE_TYPE (len), NULL),
+                                  len,
+                                  build_int_cst (TREE_TYPE (len), 1));
   gimple_set_location (stmt, loc);
-  gsi_insert_after (&gsi, stmt, GSI_NEW_STMT);
-
-  build_check_stmt (loc, gimple_assign_lhs (stmt), &gsi,
-		    /*before_p=*/false, /*is_store=*/false, 1);
-
-  /* Ensure that iter points to the statement logically following the
-     one it was initially pointing to.  */
-  *iter = gsi;
-  /* As *ITER has been advanced to point to the next statement, let's
-     return true to inform transform_statements that it shouldn't
-     advance *ITER anymore; otherwises it will skip that next
-     statement, which wouldn't be instrumented.  */
+  gsi_insert_after (iter, stmt, GSI_NEW_STMT);
+
+  build_check_stmt (loc, gimple_assign_lhs (stmt), len, 1, iter,
+                    /*non_zero_len_p*/true, /*before_p=*/false,
+                    /*is_store=*/false, /*is_scalar_access*/false, /*align*/0);
+
   return true;
 }
 
@@ -2568,6 +2618,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..e857543 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_STOREN)
 		    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..b4af164 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_LOAD1, "__asan_load1",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_LOAD2, "__asan_load2",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_LOAD4, "__asan_load4",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_LOAD8, "__asan_load8",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_LOAD16, "__asan_load16",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_LOADN, "__asan_loadN",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_STORE1, "__asan_store1",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_STORE2, "__asan_store2",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_STORE4, "__asan_store4",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_STORE8, "__asan_store8",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_STORE16, "__asan_store16",
+		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_STOREN, "__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 } } */
+
diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-1.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-1.c
index fa52e0c..c7c594e 100644
--- a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-1.c
+++ b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-1.c
@@ -16,12 +16,11 @@ test0 ()
   tab[0] = 1;
   tab[1] = 2;
 
-  /* __builtin___asan_report_load1 called 1 time for the store
-     below.  */
-  char t0 = tab[1];
-
   /* This load should not be instrumented because it is to the same
      memory location as above.  */
+  char t0 = tab[1];
+
+  /* Likewise.  */
   char t1 = tab[1];
 
   return t0 + t1;
@@ -36,7 +35,7 @@ test1 (int i)
     the initialization.  */
   foo[i] = 1;
 
-  /*__builtin___asan_report_store1 called 2 times here to instrument
+  /*__builtin___asan_report_store_n called once here to instrument
     the store to the memory region of tab.  */
   __builtin_memset (tab, 3, sizeof (tab));
 
@@ -44,8 +43,8 @@ test1 (int i)
   __builtin_memset (tab, 4, sizeof (tab));
   __builtin_memset (tab, 5, sizeof (tab));
 
-  /* There are 2 calls to __builtin___asan_report_store1 and 2 calls
-     to __builtin___asan_report_load1 to instrument the store to
+  /* There is a call to __builtin___asan_report_store_n and a call
+     to __builtin___asan_report_load_n to instrument the store to
      (subset of) the memory region of tab.  */
   __builtin_memcpy (&tab[1], foo + i, 3);
 
@@ -53,7 +52,7 @@ test1 (int i)
      the reference to tab[1] has been already instrumented above.  */
   return tab[1];
 
-  /* So for these function, there should be 7 calls to
+  /* So for these functions, there should be 3 calls to
      __builtin___asan_report_store1.  */
 }
 
@@ -63,6 +62,7 @@ main ()
   return test0 () && test1 (0);
 }
 
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store1" 7 "asan0" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load" 2 "asan0" }  } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store1" 3 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store_n" 2 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load" 1 "asan0" }  } */
 /* { dg-final { cleanup-tree-dump "asan0" } } */
diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-2.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-2.c
index 28525e0..143312f 100644
--- a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-2.c
+++ b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-2.c
@@ -20,6 +20,7 @@ main ()
   __builtin_memset (tab, 1, 3);
 }
 
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store1" 3 "asan0" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report" 3 "asan0" }  } */
+/* { dg-final { scan-tree-dump-times "& 7" 3 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store_n" 2 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report" 2 "asan0" }  } */
 /* { dg-final { cleanup-tree-dump "asan0" } } */
diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-4.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-4.c
index b2e7284..da91cd5 100644
--- a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-4.c
+++ b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-4.c
@@ -5,9 +5,13 @@
 void
 foo  (int *a, char *b, char *c)
 {
+  /* One check for c[0], one check for a[], one check for c, two checks for b.  */
   __builtin_memmove (c, b, a[c[0]]);
+  /* For a total of 5 checks.  */
 }
 
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 3 "asan0" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store1" 1 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "& 7" 5 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 1 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load_n" 1 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store_n" 1 "asan0" } } */
 /* { dg-final { cleanup-tree-dump "asan0" } } */
diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-5.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-5.c
index ead3f58..134be66 100644
--- a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-5.c
+++ b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-5.c
@@ -5,9 +5,14 @@
 void
 foo  (int *a, char *b, char *c)
 {
+  /* One check for b[0], one check for a[], 2 checks for c and one checks for b.  */
   __builtin_memmove (c, b, a[b[0]]);
+  /* For a total of 5 checks.  */
 }
 
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 2 "asan0" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store1" 2 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "& 7" 5 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 1 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load4" 1 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load_n" 1 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store_n" 1 "asan0" } } */
 /* { dg-final { cleanup-tree-dump "asan0" } } */
diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-6.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-6.c
index e4691bc..6e7ed25 100644
--- a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-6.c
+++ b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-6.c
@@ -5,10 +5,16 @@
 void
 foo  (int *a, char *b, char *c)
 {
+  /* One check for c[0], one check for a[], one check for c and 2 checks for b.   */
   __builtin_memmove (c, b, a[c[0]]);
+  /* One check for a[], one check for c and one check for b.  */
   __builtin_memmove (c, b, a[b[0]]);
+  /* For a total of 8 checks.  */
 }
 
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 5 "asan0" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store1" 2 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "& 7" 8 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 1 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load4" 2 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load_n" 2 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store_n" 2 "asan0" } } */
 /* { dg-final { cleanup-tree-dump "asan0" } } */
diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-7.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-7.c
index bf40a03..a04956d 100644
--- a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-7.c
+++ b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-7.c
@@ -13,11 +13,15 @@ struct S
 int
 foo  (int *a, char *b, char *c)
 {
+  /* 2 checks for s.a, 2 checks for e.  */
   int d = __builtin_memcmp (s.a, e, 100);
+  /* One check for s.a and one check for e.  */
   d += __builtin_memcmp (s.a, e, 200);
+  /* For a total of 6 checks.  */
   return d;
 }
 
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load" 6 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "& 7" 6 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load_n" 4 "asan0" } } */
 /* { dg-final { scan-tree-dump-not "__builtin___asan_report_store" "asan0" } } */
 /* { dg-final { cleanup-tree-dump "asan0" } } */
diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-8.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-8.c
index 38ea7a2..308a133 100644
--- a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-8.c
+++ b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-8.c
@@ -5,10 +5,16 @@
 char
 foo  (int *a, char *b, char *c)
 {
+  /* One check for b[0], one check for a[], two checks for c and one check for b.  */
   __builtin_memmove (c, b, a[b[0]]);
+  /* No checks here.  */
   return c[0] + b[0];
+  /* For a total of 5 checks.  */
 }
 
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 3 "asan0" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store1" 2 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "& 7" 5 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 1 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load4" 1 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load_n" 1 "asan0" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store_n" 1 "asan0" } } */
 /* { dg-final { cleanup-tree-dump "asan0" } } */

Reply via email to