Hi all,

This patch adds support for outline Asan instrumentation (i.e. function calls instead of inline checks). This 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

Implementation is a bit different from LLVM: GCC starts generating function calls after overflowing the threshold whereas LLVM replaces all instrumentations once total number is estimated to be larger than treshold. Making implementations more similar would require bigger rewrite of Asan which I would prefer to avoid.

The patch consists of two parts:
* asan_negative_params_1.diff - support for negative parameters
* asan_calls_1.diff - the proper

Bootstrapped/regtested on x64.

-Y
2014-04-26  Yury Gribov  <y.gri...@samsung.com>

	gcc/
	* config/s390/s390.c (s390_option_override): Rename function.
	* config/sh/sh.c: Likewise.
	* opts-common.c (unsigned_integral_argument): New function.
	(integral_argument): Add support for signed values.
	(decode_cmdline_option): Rename function.
	* opts.c (default_options_optimization): Rename function.
	(handle_param): Allow signed --param values.
	(set_debug_level): Rename function.
	* opts.h: Update prototypes.
	* params.c (set_param_value): Remove check for unsigned.
	* params.h (INVALID_PARAM_VAL): Remove macro.

diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c
index 417e2a8..4e184d5 100644
--- a/gcc/config/s390/s390.c
+++ b/gcc/config/s390/s390.c
@@ -11853,7 +11853,7 @@ s390_option_override (void)
 	    {
 	      int val;
 
-	      val = integral_argument (opt->arg);
+	      val = unsigned_integral_argument (opt->arg);
 	      if (val == -1)
 		{
 		  /* argument is not a plain number */
diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c
index a0c4628..7565953 100644
--- a/gcc/config/sh/sh.c
+++ b/gcc/config/sh/sh.c
@@ -710,7 +710,7 @@ got_mode_name:;
       else if (tokens[i].find ("gbr-offset=") == 0)
 	{
 	  std::string offset_str = tokens[i].substr (strlen ("gbr-offset="));
-	  ret.tcb_gbr_offset = integral_argument (offset_str.c_str ());
+	  ret.tcb_gbr_offset = unsigned_integral_argument (offset_str.c_str ());
 	  if (offset_str.empty () || ret.tcb_gbr_offset == -1)
 	    err_ret ("could not parse gbr-offset value \"%s\" in atomic model "
 		     "option", offset_str.c_str ());
diff --git a/gcc/opts-common.c b/gcc/opts-common.c
index 007a546..737c66b 100644
--- a/gcc/opts-common.c
+++ b/gcc/opts-common.c
@@ -151,10 +151,27 @@ find_opt (const char *input, unsigned int lang_mask)
    value, otherwise return -1.  */
 
 int
-integral_argument (const char *arg)
+unsigned_integral_argument (const char *arg)
+{
+  int error;
+  int val = integral_argument(arg, &error);
+  return error || val < 0 ? -1 : val;
+}
+
+/* If ARG is a decimal or hexadecimal integer, return its
+   value, otherwise return -1 and set ERROR.  */
+
+int
+integral_argument (const char *arg, int *error)
 {
   const char *p = arg;
 
+  if (error)
+    *error = 0;
+
+  if (*p == '-')
+    p++;
+
   while (*p && ISDIGIT (*p))
     p++;
 
@@ -162,16 +179,29 @@ integral_argument (const char *arg)
     return atoi (arg);
 
   /* It wasn't a decimal number - try hexadecimal.  */
-  if (arg[0] == '0' && (arg[1] == 'x' || arg[1] == 'X'))
+
+  p = arg;
+
+  if (*p == '-')
+    p++;
+
+  if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))
     {
-      p = arg + 2;
-      while (*p && ISXDIGIT (*p))
-	p++;
+      int has_digits = 0;
+
+      p += 2;
+
+      while (*p && ISXDIGIT (*p)) {
+	  has_digits = 1;
+	  p++;
+      }
 
-      if (p != arg + 2 && *p == '\0')
+      if (has_digits && *p == '\0')
 	return strtol (arg, NULL, 16);
     }
 
+  if (error)
+    *error = 1;
   return -1;
 }
 
@@ -610,7 +640,7 @@ decode_cmdline_option (const char **argv, unsigned int lang_mask,
   /* If the switch takes an integer, convert it.  */
   if (arg && option->cl_uinteger)
     {
-      value = integral_argument (arg);
+      value = unsigned_integral_argument (arg);
       if (value == -1)
 	errors |= CL_ERR_UINT_ARG;
     }
diff --git a/gcc/opts.c b/gcc/opts.c
index 2f4f913..7687271 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -551,7 +551,7 @@ default_options_optimization (struct gcc_options *opts,
 	    }
 	  else
 	    {
-	      const int optimize_val = integral_argument (opt->arg);
+	      const int optimize_val = unsigned_integral_argument (opt->arg);
 	      if (optimize_val == -1)
 		error_at (loc, "argument to %<-O%> should be a non-negative "
 			       "integer, %<g%>, %<s%> or %<fast%>");
@@ -1932,8 +1932,9 @@ handle_param (struct gcc_options *opts, struct gcc_options *opts_set,
 	      arg);
   else
     {
-      value = integral_argument (equal + 1);
-      if (value == -1)
+      int error;
+      value = integral_argument (equal + 1, &error);
+      if (error)
 	error_at (loc, "invalid --param value %qs", equal + 1);
       else
 	{
@@ -2077,7 +2078,7 @@ set_debug_level (enum debug_info_type type, int extended, const char *arg,
     }
   else
     {
-      int argval = integral_argument (arg);
+      int argval = unsigned_integral_argument (arg);
       if (argval == -1)
 	error_at (loc, "unrecognised debug output level \"%s\"", arg);
       else if (argval > 3)
diff --git a/gcc/opts.h b/gcc/opts.h
index f694082..448e35f 100644
--- a/gcc/opts.h
+++ b/gcc/opts.h
@@ -313,7 +313,8 @@ extern char *opts_concat (const char *first, ...);
 extern struct obstack opts_obstack;
 
 size_t find_opt (const char *input, unsigned int lang_mask);
-extern int integral_argument (const char *arg);
+extern int integral_argument (const char *arg, int *error);
+extern int unsigned_integral_argument (const char *arg);
 extern bool enum_value_to_arg (const struct cl_enum_arg *enum_args,
 			       const char **argp, int value,
 			       unsigned int lang_mask);
diff --git a/gcc/params.c b/gcc/params.c
index 3ae5ccd..3d693ed 100644
--- a/gcc/params.c
+++ b/gcc/params.c
@@ -109,9 +109,6 @@ set_param_value (const char *name, int value,
 {
   size_t i;
 
-  /* Make sure nobody tries to set a parameter to an invalid value.  */
-  gcc_assert (value != INVALID_PARAM_VAL);
-
   /* Scan the parameter table to find a matching entry.  */
   for (i = 0; i < num_compiler_params; ++i)
     if (strcmp (compiler_params[i].option, name) == 0)
diff --git a/gcc/params.h b/gcc/params.h
index 0d6daa2..369ef10 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -32,10 +32,6 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_PARAMS_H
 #define GCC_PARAMS_H
 
-/* No parameter shall have this value.  */
-
-#define INVALID_PARAM_VAL (-1)
-
 /* The information associated with each parameter.  */
 
 struct param_info
2014-04-26  Yury Gribov  <y.gri...@samsung.com>
    
	New asan-instrumentation-with-call-threshold parameter.
    
	gcc/
	* asan.c (check_func): New function.
	(check_sized_func): Likewise.
	(build_check_stmt_sized): Likewise.
	(build_check_stmt): Add support for new parameter.
	(instrument_mem_region_access): Likewise.
	(initialize_sanitizer_builtins): 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.

diff --git a/gcc/asan.c b/gcc/asan.c
index 118f9fc..96a97f2 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.  */
 
@@ -1332,6 +1334,34 @@ report_error_func (bool is_store, int size_in_bytes)
   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}.
+   IS_STORE is either 1 (for a store) or 0 (for a load).
+   SIZE_IN_BYTES is one of 1, 2, 4, 8, 16.  */
+
+static tree
+check_func (bool is_store, int size_in_bytes)
+{
+  static enum built_in_function check[2][5]
+    = { { 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_STORE1, BUILT_IN_ASAN_CHECK_STORE2,
+	  BUILT_IN_ASAN_CHECK_STORE4, BUILT_IN_ASAN_CHECK_STORE8,
+	  BUILT_IN_ASAN_CHECK_STORE16 } };
+  return builtin_decl_implicit (check[is_store][exact_log2 (size_in_bytes)]);
+}
+
+/* Construct a function tree for __asan_{load,store}N.
+   IS_STORE is either 1 (for a store) or 0 (for a load). */
+
+static tree
+check_sized_func (bool is_store)
+{
+  static enum built_in_function check_sized[2]
+    = { BUILT_IN_ASAN_CHECK_LOADN, BUILT_IN_ASAN_CHECK_STOREN };
+  return builtin_decl_implicit (check_sized[is_store]);
+}
+
 /* 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
@@ -1476,17 +1506,30 @@ 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;
+  bool use_calls = asan_num_accesses > ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD;
 
-  /* 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.  */
@@ -1507,6 +1550,19 @@ 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)
+    {
+      g = gimple_build_call (check_func (is_store, size_in_bytes),
+			 1, base_addr);
+      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 ().  */
 
@@ -1580,6 +1636,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_sized_func (is_store),
+			 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).  */
@@ -1687,6 +1798,8 @@ 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))
@@ -1708,6 +1821,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
@@ -2265,6 +2386,9 @@ initialize_sanitizer_builtins (void)
   tree BT_FN_VOID_PTR_PTR
     = build_function_type_list (void_type_node, ptr_type_node,
 				ptr_type_node, NULL_TREE);
+  tree BT_FN_VOID_PTR_SIZE
+    = build_function_type_list (void_type_node, ptr_type_node,
+				size_type_node, NULL_TREE);
   tree BT_FN_VOID_PTR_PTR_PTR
     = build_function_type_list (void_type_node, ptr_type_node,
 				ptr_type_node, ptr_type_node, NULL_TREE);
@@ -2481,6 +2605,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..ed9f7f1 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_STOREN)
 		    return dir_none;
 		}
 	    }
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index fcdcb1d..5114143 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -10222,6 +10222,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
+If the function being instrumented contains more than this number of
+memory accesses, use callbacks instead of instead of generating inline
+code. 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..f26cfd5 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 "
+         "exceeds this threshold",
+         10000, -1, 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 369ef10..6fd5b55 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -228,5 +228,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 a2f7ff0..6a8ef30 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)
@@ -51,6 +51,30 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_STORE8, "__asan_report_store8",
 		      BT_FN_VOID_PTR, ATTR_TMPURE_NORETURN_NOTHROW_LEAF_LIST)
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_STORE16, "__asan_report_store16",
 		      BT_FN_VOID_PTR, 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_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_LOADN, "__asan_loadN",
+		      BT_FN_VOID_PTR_SIZE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CHECK_STOREN, "__asan_storeN",
+		      BT_FN_VOID_PTR_SIZE, 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..4c85772
--- /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=-1 -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 } } */

Reply via email to