[PATCH] Add 'no_builtin' function attribute

Keith Packard keithp@keithp.com
Tue Nov 2 21:14:58 GMT 2021


This attribute controls optimizations which make assumptions about the
semantics of builtin functions. Typical cases here are code which
match memcpy, calloc, sincos, or which call builtins like free.

This extends on things like the -ftree-loop-distribute-patterns
flag. That flag only covers converting loops into builtin calls, not
numerous other places where knowledge of builtin function semantics
changes the generated code.

The goal is to allow built-in functions to be declared by the compiler
and used directly by the application, but to disable optimizations
which take advantage of compiler knowledge about their semantics, and
to allow this optimization behavior to be changed for individual
functions.

One place where this behavior is especially useful is when compiling
the builtin functions that gcc knows about, as in the C
library. Currently, C library source code and build systems have
various kludges to work around the compilers operations in these
areas, using a combination of -fno-tree-loop-distribute-patterns,
-fno-builtins and even symbol aliases to keep GCC from generating
infinite recursions.

This can be applied globally to a file using the -fno-optimize-builtin
flag.

This disables optimizations which translate a sequence of builtin calls
into an equivalent sequence:

	void
	attribute((no_builtin))
	sincos(double x, double *s, double *c)
	{
		*s = sin(x);
		*c = cos(x);
	}

This also avoids converting loops into builtin calls like this:

	void *
	attribute((no_builtin))
	memcpy(void *__restrict__ dst, const void *__restrict__ src, size_t n)
	{
		char *d = dst;
		const char *s = src;

		while (n--)
			*d++ = *s++;
		return dst;
	}

As well as disabling analysis of memory lifetimes around free as in
this example:

	void *
	attribute((no_builtin))
	erase_and_free(void *ptr)
	{
		memset(ptr, '\0', malloc_usable_size(ptr));
		free(ptr);
	}

It also prevents converting builtin calls into inline code:

	void
	attribute((no_builtin))
	copy_fixed(char *dest)
	{
		strcpy(dest, "hello world");
	}

Clang has a more sophisticated version of this mechanism which
can disable specific builtins:

	double
	attribute((no_builtin("exp2")))
	exp2(double x)
	{
		return pow (2.0, x);
	}

The general approach in this change is to introduce checks in some
places where builtin functions are used to see if the specific
function is 'allowed' to be used for optimization, skipping the
optimization when the desired function has been disabled.

Three new functions, builtin_decl_implicit_opt_p,
builtin_decl_explicit_opt and builtin_decl_implicit_opt are introduced
which add checks for whether the compiler can assume standard
semantics for the specified function for purposes of
optimization. These are used throughout the compiler wherever
appropriate. Code which must use builtins for correct operation
(e.g. struct assignment) are not affected.

The machinery proposed here could be extended to support the
additional clang feature by extending the attribute parsing function
and creating a list of disabled builtins checked by the builtin_decl
functions described above.

Signed-off-by: Keith Packard <keithp@keithp.com>
---
 gcc/builtins.c               | 12 +++---
 gcc/c-family/c-attribs.c     | 68 ++++++++++++++++++++++++++++++++++
 gcc/common.opt               |  4 ++
 gcc/gimple-fold.c            | 72 ++++++++++++++++++------------------
 gcc/gimple-match-head.c      |  2 +-
 gcc/tree-loop-distribution.c |  7 ++++
 gcc/tree-ssa-alias.c         |  3 +-
 gcc/tree-ssa-strlen.c        | 48 ++++++++++++++----------
 gcc/tree-ssa-structalias.c   |  3 +-
 gcc/tree.h                   | 39 +++++++++++++++++++
 10 files changed, 194 insertions(+), 64 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 7d0f61fc98b..d665ee716e8 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -2061,7 +2061,7 @@ mathfn_built_in_1 (tree type, combined_fn fn, bool implicit_p)
   if (fcode2 == END_BUILTINS)
     return NULL_TREE;
 
-  if (implicit_p && !builtin_decl_implicit_p (fcode2))
+  if (implicit_p && !builtin_decl_implicit_opt_p (fcode2))
     return NULL_TREE;
 
   return builtin_decl_explicit (fcode2);
@@ -3481,9 +3481,9 @@ expand_builtin_stpcpy_1 (tree exp, rtx target, machine_mode mode)
   src = CALL_EXPR_ARG (exp, 1);
 
   /* If return value is ignored, transform stpcpy into strcpy.  */
-  if (target == const0_rtx && builtin_decl_implicit (BUILT_IN_STRCPY))
+  if (target == const0_rtx && builtin_decl_implicit_opt (BUILT_IN_STRCPY))
     {
-      tree fn = builtin_decl_implicit (BUILT_IN_STRCPY);
+      tree fn = builtin_decl_implicit_opt (BUILT_IN_STRCPY);
       tree result = build_call_nofold_loc (loc, fn, 2, dst, src);
       return expand_expr (result, target, mode, EXPAND_NORMAL);
     }
@@ -8258,7 +8258,7 @@ fold_builtin_sincos (location_t loc,
   if (!call)
     {
       if (!targetm.libc_has_function (function_c99_math_complex, type)
-	  || !builtin_decl_implicit_p (fn))
+	  || !builtin_decl_implicit_opt_p (fn))
 	return NULL_TREE;
       fndecl = builtin_decl_explicit (fn);
       call = build_call_expr_loc (loc, fndecl, 1, arg0);
@@ -9717,7 +9717,7 @@ fold_builtin_strpbrk (location_t loc, tree, tree s1, tree s2, tree type)
   if (p2[1] != '\0')
     return NULL_TREE;  /* Really call strpbrk.  */
 
-  fn = builtin_decl_implicit (BUILT_IN_STRCHR);
+  fn = builtin_decl_implicit_opt (BUILT_IN_STRCHR);
   if (!fn)
     return NULL_TREE;
 
@@ -9810,7 +9810,7 @@ fold_builtin_strcspn (location_t loc, tree expr, tree s1, tree s2)
   const char *p2 = c_getstr (s2);
   if (p2 && *p2 == '\0')
     {
-      tree fn = builtin_decl_implicit (BUILT_IN_STRLEN);
+      tree fn = builtin_decl_implicit_opt (BUILT_IN_STRLEN);
 
       /* If the replacement _DECL isn't initialized, don't do the
 	 transformation.  */
diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 007b928c54b..d5cfa9bc70d 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -130,6 +130,7 @@ static tree handle_vector_size_attribute (tree *, tree, tree, int,
 					  bool *) ATTRIBUTE_NONNULL(3);
 static tree handle_nonnull_attribute (tree *, tree, tree, int, bool *);
 static tree handle_nonstring_attribute (tree *, tree, tree, int, bool *);
+static tree handle_no_builtin_attribute (tree *, tree, tree, int, bool *);
 static tree handle_nothrow_attribute (tree *, tree, tree, int, bool *);
 static tree handle_cleanup_attribute (tree *, tree, tree, int, bool *);
 static tree handle_warn_unused_result_attribute (tree *, tree, tree, int,
@@ -536,6 +537,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_special_var_sec_attribute, attr_section_exclusions },
   { "access",		      1, 3, false, true, true, false,
 			      handle_access_attribute, NULL },
+  { "no_builtin",	      0, 0, true, false, false, false,
+			      handle_no_builtin_attribute, NULL },
   /* Attributes used by Objective-C.  */
   { "NSObject",		      0, 0, true, false, false, false,
 			      handle_nsobject_attribute, NULL },
@@ -5512,6 +5515,71 @@ handle_optimize_attribute (tree *node, tree name, tree args,
   return NULL_TREE;
 }
 
+/* Handle a "no_builtin" attribute */
+
+static tree
+handle_no_builtin_attribute (tree *node, tree name, tree ARG_UNUSED (args),
+			  int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  /* Ensure we have a function type.  */
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+  else
+    {
+      struct cl_optimization cur_opts;
+      tree old_opts = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (*node);
+
+      /* Save current options.  */
+      cl_optimization_save (&cur_opts, &global_options, &global_options_set);
+      tree prev_target_node = build_target_option_node (&global_options,
+							&global_options_set);
+
+      /* If we previously had some optimization options, use them as the
+	 default.  */
+      gcc_options *saved_global_options = NULL;
+
+      /* When #pragma GCC optimize pragma is used, it modifies global_options
+	 without calling targetm.override_options_after_change.  That can leave
+	 target flags inconsistent for comparison.  */
+      if (flag_checking && optimization_current_node == optimization_default_node)
+	{
+	  saved_global_options = XNEW (gcc_options);
+	  *saved_global_options = global_options;
+	}
+
+      if (old_opts)
+	cl_optimization_restore (&global_options, &global_options_set,
+				 TREE_OPTIMIZATION (old_opts));
+
+      /* Set no_builtin option */
+      flag_no_opt_builtin = TRUE;
+
+      DECL_FUNCTION_SPECIFIC_OPTIMIZATION (*node)
+	= build_optimization_node (&global_options, &global_options_set);
+      tree target_node = build_target_option_node (&global_options,
+						   &global_options_set);
+      if (prev_target_node != target_node)
+	DECL_FUNCTION_SPECIFIC_TARGET (*node) = target_node;
+
+      /* Restore current options.  */
+      cl_optimization_restore (&global_options, &global_options_set,
+			       &cur_opts);
+      cl_target_option_restore (&global_options, &global_options_set,
+				TREE_TARGET_OPTION (prev_target_node));
+
+      if (saved_global_options != NULL)
+	{
+	  cl_optimization_compare (saved_global_options, &global_options);
+	  free (saved_global_options);
+	}
+    }
+
+  return NULL_TREE;
+}
+
 /* Handle a "no_split_stack" attribute.  */
 
 static tree
diff --git a/gcc/common.opt b/gcc/common.opt
index eeba1a727f2..a5ec18e82f5 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -2154,6 +2154,10 @@ fsave-optimization-record
 Common Var(flag_save_optimization_record) Optimization
 Write a SRCFILE.opt-record.json file detailing what optimizations were performed.
 
+foptimize-builtin
+Common Var(flag_no_opt_builtin, 0) Optimization
+Enable optimizations based on known semantics for builtin functions.
+
 foptimize-register-move
 Common Ignore
 Does nothing. Preserved for backward compatibility.
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 6e25a7c05db..b054f4248ce 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -1071,7 +1071,7 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 		  && (MIN (src_align, dest_align) / BITS_PER_UNIT
 		      >= tree_to_uhwi (len))))
 	    {
-	      tree fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+	      tree fn = builtin_decl_implicit_opt (BUILT_IN_MEMCPY);
 	      if (!fn)
 		return false;
 	      gimple_call_set_fndecl (stmt, fn);
@@ -1125,7 +1125,7 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	      else
 		return false;
 
-	      fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+	      fn = builtin_decl_implicit_opt (BUILT_IN_MEMCPY);
 	      if (!fn)
 		return false;
 	      gimple_call_set_fndecl (stmt, fn);
@@ -1148,7 +1148,7 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	      if (!refs_may_alias_p_1 (&destr, &srcr, false))
 		{
 		  tree fn;
-		  fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+		  fn = builtin_decl_implicit_opt (BUILT_IN_MEMCPY);
 		  if (!fn)
 		    return false;
 		  gimple_call_set_fndecl (stmt, fn);
@@ -1360,7 +1360,7 @@ done:
 static bool
 gimple_fold_builtin_bcmp (gimple_stmt_iterator *gsi)
 {
-  tree fn = builtin_decl_implicit (BUILT_IN_MEMCMP);
+  tree fn = builtin_decl_implicit_opt (BUILT_IN_MEMCMP);
 
   if (!fn)
     return false;
@@ -1384,7 +1384,7 @@ gimple_fold_builtin_bcmp (gimple_stmt_iterator *gsi)
 static bool
 gimple_fold_builtin_bcopy (gimple_stmt_iterator *gsi)
 {
-  tree fn = builtin_decl_implicit (BUILT_IN_MEMMOVE);
+  tree fn = builtin_decl_implicit_opt (BUILT_IN_MEMMOVE);
 
   if (!fn)
     return false;
@@ -1411,7 +1411,7 @@ gimple_fold_builtin_bcopy (gimple_stmt_iterator *gsi)
 static bool
 gimple_fold_builtin_bzero (gimple_stmt_iterator *gsi)
 {
-  tree fn = builtin_decl_implicit (BUILT_IN_MEMSET);
+  tree fn = builtin_decl_implicit_opt (BUILT_IN_MEMSET);
 
   if (!fn)
     return false;
@@ -2067,7 +2067,7 @@ gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
   if (optimize_function_for_size_p (cfun))
     return false;
 
-  fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+  fn = builtin_decl_implicit_opt (BUILT_IN_MEMCPY);
   if (!fn)
     return false;
 
@@ -2158,7 +2158,7 @@ gimple_fold_builtin_strncpy (gimple_stmt_iterator *gsi,
   maybe_diag_stxncpy_trunc (*gsi, src, len);
 
   /* OK transform into builtin memcpy.  */
-  tree fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+  tree fn = builtin_decl_implicit_opt (BUILT_IN_MEMCPY);
   if (!fn)
     return false;
 
@@ -2219,7 +2219,7 @@ gimple_fold_builtin_strchr (gimple_stmt_iterator *gsi, bool is_strrchr)
   /* Transform strrchr (s, 0) to strchr (s, 0) when optimizing for size.  */
   if (is_strrchr && optimize_function_for_size_p (cfun))
     {
-      tree strchr_fn = builtin_decl_implicit (BUILT_IN_STRCHR);
+      tree strchr_fn = builtin_decl_implicit_opt (BUILT_IN_STRCHR);
 
       if (strchr_fn)
 	{
@@ -2232,7 +2232,7 @@ gimple_fold_builtin_strchr (gimple_stmt_iterator *gsi, bool is_strrchr)
     }
 
   tree len;
-  tree strlen_fn = builtin_decl_implicit (BUILT_IN_STRLEN);
+  tree strlen_fn = builtin_decl_implicit_opt (BUILT_IN_STRLEN);
 
   if (!strlen_fn)
     return false;
@@ -2314,7 +2314,7 @@ gimple_fold_builtin_strstr (gimple_stmt_iterator *gsi)
   /* Transform strstr (x, "c") into strchr (x, 'c').  */
   if (q[1] == '\0')
     {
-      tree strchr_fn = builtin_decl_implicit (BUILT_IN_STRCHR);
+      tree strchr_fn = builtin_decl_implicit_opt (BUILT_IN_STRCHR);
       if (strchr_fn)
 	{
 	  tree c = build_int_cst (integer_type_node, q[0]);
@@ -2365,8 +2365,8 @@ gimple_fold_builtin_strcat (gimple_stmt_iterator *gsi, tree dst, tree src)
 
   /* See if we can store by pieces into (dst + strlen(dst)).  */
   tree newdst;
-  tree strlen_fn = builtin_decl_implicit (BUILT_IN_STRLEN);
-  tree memcpy_fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+  tree strlen_fn = builtin_decl_implicit_opt (BUILT_IN_STRLEN);
+  tree memcpy_fn = builtin_decl_implicit_opt (BUILT_IN_MEMCPY);
 
   if (!strlen_fn || !memcpy_fn)
     return false;
@@ -2445,7 +2445,7 @@ gimple_fold_builtin_strcat_chk (gimple_stmt_iterator *gsi)
     return false;
 
   /* If __builtin_strcat_chk is used, assume strcat is available.  */
-  fn = builtin_decl_explicit (BUILT_IN_STRCAT);
+  fn = builtin_decl_explicit_opt (BUILT_IN_STRCAT);
   if (!fn)
     return false;
 
@@ -2530,7 +2530,7 @@ gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
 	suppress_warning (stmt, OPT_Wstringop_overflow_);
     }
 
-  tree fn = builtin_decl_implicit (BUILT_IN_STRCAT);
+  tree fn = builtin_decl_implicit_opt (BUILT_IN_STRCAT);
 
   /* If the replacement _DECL isn't initialized, don't do the
      transformation.  */
@@ -2578,7 +2578,7 @@ gimple_fold_builtin_strncat_chk (gimple_stmt_iterator *gsi)
 	  && ! tree_int_cst_lt (len, src_len))
 	{
 	  /* If LEN >= strlen (SRC), optimize into __strcat_chk.  */
-	  fn = builtin_decl_explicit (BUILT_IN_STRCAT_CHK);
+	  fn = builtin_decl_explicit_opt (BUILT_IN_STRCAT_CHK);
 	  if (!fn)
 	    return false;
 
@@ -2590,7 +2590,7 @@ gimple_fold_builtin_strncat_chk (gimple_stmt_iterator *gsi)
     }
 
   /* If __builtin_strncat_chk is used, assume strncat is available.  */
-  fn = builtin_decl_explicit (BUILT_IN_STRNCAT);
+  fn = builtin_decl_explicit_opt (BUILT_IN_STRNCAT);
   if (!fn)
     return false;
 
@@ -2829,7 +2829,7 @@ gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
       && ((p2 && len2 < bound && len2 == nulpos2)
 	  || (p1 && len1 < bound && len1 == nulpos1)))
     {
-      tree fn = builtin_decl_implicit (BUILT_IN_STRCMP);
+      tree fn = builtin_decl_implicit_opt (BUILT_IN_STRCMP);
       if (!fn)
         return false;
       gimple *repl = gimple_build_call (fn, 2, str1, str2);
@@ -2927,11 +2927,11 @@ gimple_fold_builtin_fputs (gimple_stmt_iterator *gsi,
   /* If we're using an unlocked function, assume the other unlocked
      functions exist explicitly.  */
   tree const fn_fputc = (unlocked
-			 ? builtin_decl_explicit (BUILT_IN_FPUTC_UNLOCKED)
-			 : builtin_decl_implicit (BUILT_IN_FPUTC));
+			 ? builtin_decl_explicit_opt (BUILT_IN_FPUTC_UNLOCKED)
+			 : builtin_decl_implicit_opt (BUILT_IN_FPUTC));
   tree const fn_fwrite = (unlocked
-			  ? builtin_decl_explicit (BUILT_IN_FWRITE_UNLOCKED)
-			  : builtin_decl_implicit (BUILT_IN_FWRITE));
+			  ? builtin_decl_explicit_opt (BUILT_IN_FWRITE_UNLOCKED)
+			  : builtin_decl_implicit_opt (BUILT_IN_FWRITE));
 
   /* If the return value is used, don't do the transformation.  */
   if (gimple_call_lhs (stmt))
@@ -3041,7 +3041,7 @@ gimple_fold_builtin_memory_chk (gimple_stmt_iterator *gsi,
 		{
 		  /* (void) __mempcpy_chk () can be optimized into
 		     (void) __memcpy_chk ().  */
-		  fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK);
+		  fn = builtin_decl_explicit_opt (BUILT_IN_MEMCPY_CHK);
 		  if (!fn)
 		    return false;
 
@@ -3269,7 +3269,7 @@ gimple_fold_builtin_stpcpy (gimple_stmt_iterator *gsi)
   /* If the result is unused, replace stpcpy with strcpy.  */
   if (gimple_call_lhs (stmt) == NULL_TREE)
     {
-      tree fn = builtin_decl_implicit (BUILT_IN_STRCPY);
+      tree fn = builtin_decl_implicit_opt (BUILT_IN_STRCPY);
       if (!fn)
 	return false;
       gimple_call_set_fndecl (stmt, fn);
@@ -3309,7 +3309,7 @@ gimple_fold_builtin_stpcpy (gimple_stmt_iterator *gsi)
     return false;
 
   /* If the source has a known length replace stpcpy with memcpy.  */
-  fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+  fn = builtin_decl_implicit_opt (BUILT_IN_MEMCPY);
   if (!fn)
     return false;
 
@@ -3544,7 +3544,7 @@ gimple_fold_builtin_sprintf (gimple_stmt_iterator *gsi)
   if (!init_target_chars ())
     return false;
 
-  tree fn = builtin_decl_implicit (BUILT_IN_STRCPY);
+  tree fn = builtin_decl_implicit_opt (BUILT_IN_STRCPY);
   if (!fn)
     return false;
 
@@ -3679,7 +3679,7 @@ gimple_fold_builtin_snprintf (gimple_stmt_iterator *gsi)
   /* If the format doesn't contain % args or %%, use strcpy.  */
   if (strchr (fmt_str, target_percent) == NULL)
     {
-      tree fn = builtin_decl_implicit (BUILT_IN_STRCPY);
+      tree fn = builtin_decl_implicit_opt (BUILT_IN_STRCPY);
       if (!fn)
 	return false;
 
@@ -3726,7 +3726,7 @@ gimple_fold_builtin_snprintf (gimple_stmt_iterator *gsi)
   /* If the format is "%s", use strcpy if the result isn't used.  */
   else if (fmt_str && strcmp (fmt_str, target_percent_s) == 0)
     {
-      tree fn = builtin_decl_implicit (BUILT_IN_STRCPY);
+      tree fn = builtin_decl_implicit_opt (BUILT_IN_STRCPY);
       if (!fn)
 	return false;
 
@@ -3809,13 +3809,13 @@ gimple_fold_builtin_fprintf (gimple_stmt_iterator *gsi,
     {
       /* If we're using an unlocked function, assume the other
 	 unlocked functions exist explicitly.  */
-      fn_fputc = builtin_decl_explicit (BUILT_IN_FPUTC_UNLOCKED);
-      fn_fputs = builtin_decl_explicit (BUILT_IN_FPUTS_UNLOCKED);
+      fn_fputc = builtin_decl_explicit_opt (BUILT_IN_FPUTC_UNLOCKED);
+      fn_fputs = builtin_decl_explicit_opt (BUILT_IN_FPUTS_UNLOCKED);
     }
   else
     {
-      fn_fputc = builtin_decl_implicit (BUILT_IN_FPUTC);
-      fn_fputs = builtin_decl_implicit (BUILT_IN_FPUTS);
+      fn_fputc = builtin_decl_implicit_opt (BUILT_IN_FPUTC);
+      fn_fputs = builtin_decl_implicit_opt (BUILT_IN_FPUTS);
     }
 
   if (!init_target_chars ())
@@ -3909,13 +3909,13 @@ gimple_fold_builtin_printf (gimple_stmt_iterator *gsi, tree fmt,
     {
       /* If we're using an unlocked function, assume the other
 	 unlocked functions exist explicitly.  */
-      fn_putchar = builtin_decl_explicit (BUILT_IN_PUTCHAR_UNLOCKED);
-      fn_puts = builtin_decl_explicit (BUILT_IN_PUTS_UNLOCKED);
+      fn_putchar = builtin_decl_explicit_opt (BUILT_IN_PUTCHAR_UNLOCKED);
+      fn_puts = builtin_decl_explicit_opt (BUILT_IN_PUTS_UNLOCKED);
     }
   else
     {
-      fn_putchar = builtin_decl_implicit (BUILT_IN_PUTCHAR);
-      fn_puts = builtin_decl_implicit (BUILT_IN_PUTS);
+      fn_putchar = builtin_decl_implicit_opt (BUILT_IN_PUTCHAR);
+      fn_puts = builtin_decl_implicit_opt (BUILT_IN_PUTS);
     }
 
   if (!init_target_chars ())
diff --git a/gcc/gimple-match-head.c b/gcc/gimple-match-head.c
index 9d88b2f8551..82bdad1380e 100644
--- a/gcc/gimple-match-head.c
+++ b/gcc/gimple-match-head.c
@@ -630,7 +630,7 @@ maybe_push_res_to_seq (gimple_match_op *res_op, gimple_seq *seq, tree res)
       else
 	{
 	  /* Find the function we want to call.  */
-	  tree decl = builtin_decl_implicit (as_builtin_fn (fn));
+	  tree decl = builtin_decl_implicit_opt (as_builtin_fn (fn));
 	  if (!decl)
 	    return NULL;
 
diff --git a/gcc/tree-loop-distribution.c b/gcc/tree-loop-distribution.c
index 583c01a42d8..469c22d63cb 100644
--- a/gcc/tree-loop-distribution.c
+++ b/gcc/tree-loop-distribution.c
@@ -1716,6 +1716,9 @@ classify_builtin_st (loop_p loop, partition *partition, data_reference_p dr)
   gimple *stmt = DR_STMT (dr);
   tree base, size, rhs = gimple_assign_rhs1 (stmt);
 
+  if (!builtin_decl_implicit_opt_p(BUILT_IN_MEMSET))
+    return;
+
   if (const_with_all_bytes_same (rhs) == -1
       && (!INTEGRAL_TYPE_P (TREE_TYPE (rhs))
 	  || (TYPE_MODE (TREE_TYPE (rhs))
@@ -1762,6 +1765,10 @@ loop_distribution::classify_builtin_ldst (loop_p loop, struct graph *rdg,
   tree base, size, src_base, src_size;
   auto_vec<tree> dst_steps, src_steps;
 
+  if (!builtin_decl_implicit_opt_p(BUILT_IN_MEMMOVE) ||
+      !builtin_decl_implicit_opt_p(BUILT_IN_MEMCPY))
+    return;
+
   /* Compute access range of both load and store.  */
   int res = compute_access_range (loop, dst_dr, &base, &size, &dst_steps);
   if (res != 2)
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index ce667ff32b9..77cf209b235 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -3370,7 +3370,8 @@ stmt_kills_ref_p (gimple *stmt, ao_ref *ref)
     {
       tree callee = gimple_call_fndecl (stmt);
       if (callee != NULL_TREE
-	  && gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
+	  && gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
+	  && builtin_decl_implicit_opt_p(DECL_FUNCTION_CODE (callee)))
 	switch (DECL_FUNCTION_CODE (callee))
 	  {
 	  case BUILT_IN_FREE:
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 2de7cb1a6a0..050d0573936 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -860,12 +860,17 @@ get_string_length (strinfo *si)
 	 with the same stmt.  If they were unshared before and transformation
 	 has been already done, the handling of BUILT_IN_STPCPY{,_CHK} should
 	 just compute the right length.  */
+      if (!builtin_decl_implicit_opt_p (DECL_FUNCTION_CODE (callee)))
+	return NULL;
+
       switch (DECL_FUNCTION_CODE (callee))
 	{
 	case BUILT_IN_STRCAT:
 	case BUILT_IN_STRCAT_CHK:
 	  gsi = gsi_for_stmt (stmt);
-	  fn = builtin_decl_implicit (BUILT_IN_STRLEN);
+	  fn = builtin_decl_implicit_opt (BUILT_IN_STRLEN);
+	  if (!fn)
+	    return NULL;
 	  gcc_assert (lhs == NULL_TREE);
 	  tem = unshare_expr (gimple_call_arg (stmt, 0));
 	  lenstmt = gimple_build_call (fn, 1, tem);
@@ -889,11 +894,12 @@ get_string_length (strinfo *si)
 	  /* FALLTHRU */
 	case BUILT_IN_STRCPY:
 	case BUILT_IN_STRCPY_CHK:
-	  gcc_assert (builtin_decl_implicit_p (BUILT_IN_STPCPY));
 	  if (gimple_call_num_args (stmt) == 2)
-	    fn = builtin_decl_implicit (BUILT_IN_STPCPY);
+	    fn = builtin_decl_implicit_opt (BUILT_IN_STPCPY);
 	  else
-	    fn = builtin_decl_explicit (BUILT_IN_STPCPY_CHK);
+	    fn = builtin_decl_explicit_opt (BUILT_IN_STPCPY_CHK);
+	  if (!fn)
+	    return NULL;
 	  gcc_assert (lhs == NULL_TREE);
 	  if (dump_file && (dump_flags & TDF_DETAILS) != 0)
 	    {
@@ -1689,6 +1695,9 @@ valid_builtin_call (gimple *stmt)
       && !gimple_builtin_call_types_compatible_p (stmt, decl))
     return false;
 
+  if (!builtin_decl_implicit_opt_p(DECL_FUNCTION_CODE (callee)))
+    return false;
+
   switch (DECL_FUNCTION_CODE (callee))
     {
     case BUILT_IN_MEMCMP:
@@ -2519,7 +2528,7 @@ strlen_pass::handle_builtin_strcpy (built_in_function bcode)
       {
       case BUILT_IN_STRCPY:
       case BUILT_IN_STRCPY_CHK:
-	if (lhs != NULL_TREE || !builtin_decl_implicit_p (BUILT_IN_STPCPY))
+	if (lhs != NULL_TREE || !builtin_decl_implicit_opt_p (BUILT_IN_STPCPY))
 	  return;
 	break;
       case BUILT_IN_STPCPY:
@@ -2640,12 +2649,12 @@ strlen_pass::handle_builtin_strcpy (built_in_function bcode)
   switch (bcode)
     {
     case BUILT_IN_STRCPY:
-      fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+      fn = builtin_decl_implicit_opt (BUILT_IN_MEMCPY);
       if (lhs)
 	ssa_ver_to_stridx[SSA_NAME_VERSION (lhs)] = didx;
       break;
     case BUILT_IN_STRCPY_CHK:
-      fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK);
+      fn = builtin_decl_explicit_opt (BUILT_IN_MEMCPY_CHK);
       if (lhs)
 	ssa_ver_to_stridx[SSA_NAME_VERSION (lhs)] = didx;
       break;
@@ -2653,7 +2662,7 @@ strlen_pass::handle_builtin_strcpy (built_in_function bcode)
       /* This would need adjustment of the lhs (subtract one),
 	 or detection that the trailing '\0' doesn't need to be
 	 written, if it will be immediately overwritten.
-      fn = builtin_decl_explicit (BUILT_IN_MEMPCPY);  */
+      fn = builtin_decl_explicit_opt (BUILT_IN_MEMPCPY);  */
       if (lhs)
 	{
 	  dsi->endptr = lhs;
@@ -2664,7 +2673,7 @@ strlen_pass::handle_builtin_strcpy (built_in_function bcode)
       /* This would need adjustment of the lhs (subtract one),
 	 or detection that the trailing '\0' doesn't need to be
 	 written, if it will be immediately overwritten.
-      fn = builtin_decl_explicit (BUILT_IN_MEMPCPY_CHK);  */
+      fn = builtin_decl_explicit_opt (BUILT_IN_MEMPCPY_CHK);  */
       if (lhs)
 	{
 	  dsi->endptr = lhs;
@@ -3540,7 +3549,7 @@ strlen_pass::handle_builtin_strcat (built_in_function bcode)
 	 with length endptr - p if we need to compute the length
 	 later on.  Don't do this transformation if we don't need
 	 it.  */
-      if (builtin_decl_implicit_p (BUILT_IN_STPCPY) && lhs == NULL_TREE)
+      if (builtin_decl_implicit_opt_p (BUILT_IN_STPCPY) && lhs == NULL_TREE)
 	{
 	  if (didx == 0)
 	    {
@@ -3590,7 +3599,7 @@ strlen_pass::handle_builtin_strcat (built_in_function bcode)
     {
       dsi->nonzero_chars = NULL;
       dsi->full_string_p = false;
-      if (lhs == NULL_TREE && builtin_decl_implicit_p (BUILT_IN_STPCPY))
+      if (lhs == NULL_TREE && builtin_decl_implicit_opt_p (BUILT_IN_STPCPY))
 	dsi->dont_invalidate = true;
     }
 
@@ -3610,15 +3619,15 @@ strlen_pass::handle_builtin_strcat (built_in_function bcode)
     {
     case BUILT_IN_STRCAT:
       if (srclen != NULL_TREE)
-	fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+	fn = builtin_decl_implicit_opt (BUILT_IN_MEMCPY);
       else
-	fn = builtin_decl_implicit (BUILT_IN_STRCPY);
+	fn = builtin_decl_implicit_opt (BUILT_IN_STRCPY);
       break;
     case BUILT_IN_STRCAT_CHK:
       if (srclen != NULL_TREE)
-	fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK);
+	fn = builtin_decl_explicit_opt (BUILT_IN_MEMCPY_CHK);
       else
-	fn = builtin_decl_explicit (BUILT_IN_STRCPY_CHK);
+	fn = builtin_decl_explicit_opt (BUILT_IN_STRCPY_CHK);
       objsz = gimple_call_arg (stmt, 2);
       break;
     default:
@@ -3798,11 +3807,12 @@ strlen_pass::handle_builtin_memset (bool *zero_write)
   if (code1 == BUILT_IN_CALLOC)
     /* Not touching alloc_stmt */ ;
   else if (code1 == BUILT_IN_MALLOC
-	   && operand_equal_p (memset_size, alloc_size, 0))
+	   && operand_equal_p (memset_size, alloc_size, 0)
+	   && builtin_decl_implicit_opt_p (BUILT_IN_CALLOC))
     {
       /* Replace the malloc + memset calls with calloc.  */
       gimple_stmt_iterator gsi1 = gsi_for_stmt (si1->stmt);
-      update_gimple_call (&gsi1, builtin_decl_implicit (BUILT_IN_CALLOC), 2,
+      update_gimple_call (&gsi1, builtin_decl_implicit_opt (BUILT_IN_CALLOC), 2,
 			  alloc_size, build_one_cst (size_type_node));
       si1->nonzero_chars = build_int_cst (size_type_node, 0);
       si1->full_string_p = true;
@@ -4367,8 +4377,8 @@ strlen_pass::handle_builtin_string_cmp ()
       /* If the known length is less than the size of the other array
 	 and the strcmp result is only used to test equality to zero,
 	 transform the call to the equivalent _eq call.  */
-      if (tree fn = builtin_decl_implicit (bound < 0 ? BUILT_IN_STRCMP_EQ
-					   : BUILT_IN_STRNCMP_EQ))
+      if (tree fn = builtin_decl_implicit_opt (bound < 0 ? BUILT_IN_STRCMP_EQ
+					       : BUILT_IN_STRNCMP_EQ))
 	{
 	  tree n = build_int_cst (size_type_node, cmpsiz);
 	  update_gimple_call (&m_gsi, fn, 3, arg1, arg2, n);
diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c
index 99072df0768..c88ac432331 100644
--- a/gcc/tree-ssa-structalias.c
+++ b/gcc/tree-ssa-structalias.c
@@ -4415,7 +4415,8 @@ find_func_aliases_for_builtin_call (struct function *fn, gcall *t)
   auto_vec<ce_s, 4> rhsc;
   varinfo_t fi;
 
-  if (gimple_call_builtin_p (t, BUILT_IN_NORMAL))
+  if (gimple_call_builtin_p (t, BUILT_IN_NORMAL) &&
+      builtin_decl_implicit_opt_p (DECL_FUNCTION_CODE (fndecl)))
     /* ???  All builtins that are handled here need to be handled
        in the alias-oracle query functions explicitly!  */
     switch (DECL_FUNCTION_CODE (fndecl))
diff --git a/gcc/tree.h b/gcc/tree.h
index 7542d97ce12..72b969e1684 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5728,6 +5728,20 @@ builtin_decl_explicit (enum built_in_function fncode)
   return builtin_info[(size_t)fncode].decl;
 }
 
+/* Return the tree node for an explicit builtin function if the
+   optimizer is allowed to use it or NULL.  Use this instead of
+   builtin_decl_implicit in code that could introduce a new builtin
+   call to perform optimization, but where such a call is not
+   required */
+
+static inline tree
+builtin_decl_explicit_opt (enum built_in_function fncode)
+{
+  if (flag_no_opt_builtin)
+    return NULL_TREE;
+  return builtin_decl_explicit (fncode);
+}
+
 /* Return the tree node for an implicit builtin function or NULL.  */
 static inline tree
 builtin_decl_implicit (enum built_in_function fncode)
@@ -5741,6 +5755,20 @@ builtin_decl_implicit (enum built_in_function fncode)
   return builtin_info[uns_fncode].decl;
 }
 
+/* Return the tree node for an implicit builtin function if the
+   optimizer is allowed to use it or NULL. Use this instead of
+   builtin_decl_implicit in code that could introduce a new builtin
+   call to perform optimization, but where such a call is not
+   required */
+
+static inline tree
+builtin_decl_implicit_opt (enum built_in_function fncode)
+{
+  if (flag_no_opt_builtin)
+    return NULL_TREE;
+  return builtin_decl_implicit (fncode);
+}
+
 /* Set explicit builtin function nodes and whether it is an implicit
    function.  */
 
@@ -5805,6 +5833,17 @@ builtin_decl_implicit_p (enum built_in_function fncode)
 	  && builtin_info[uns_fncode].implicit_p);
 }
 
+/* Return whether the standard builtin function can be used
+   implicitly. Use this instead of builtin_decl_implicit_p in code
+   that could introduce a new builtin call to perform optimization,
+   but where such a call is not required */
+
+static inline bool
+builtin_decl_implicit_opt_p (enum built_in_function fncode)
+{
+  return !flag_no_opt_builtin && builtin_decl_implicit_p(fncode);
+}
+
 /* Return whether the standard builtin function was declared.  */
 
 static inline bool
-- 
2.33.1



More information about the Gcc-patches mailing list