[PATCH] Add 'no_builtin' function attribute

Martin Sebor msebor@gmail.com
Wed Nov 3 15:38:42 GMT 2021


On 11/2/21 3:14 PM, Keith Packard via Gcc-patches wrote:
> 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.

Can this option be used in attribute optimize?  If yes, what's
the advantage of also providing an atttribute?

A few more comments below.

> 
> 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.

It seems to me that as a matter of QOI, GCC should be able to
disable the expansion of built-ins to calls to themselves in
their bodies (I would view it as a bug if GCC expanded a byte
copying loop into a call to __builtin_memcpy in the body of
a function named memcpy, without issuing a warnings; but even
with a warning I'd hope it to do the sensible thing and avoid
introducing infinite recursion).  A compiler may not be able
to do that in calls made from those functions, and for that
the built-in expansion needs to be disabled explicitly.
-fno-builtin isn't good enough because it doesn't prevent GCC
from introiducing calls to __builtin_ functions, and that's
what this feature is for.  Do I understand correctly?

If we end up adding a new attribute (as opposed to relying
on attribute optimize) and the attribute is expected to have
an effect only on the definitions of functions and not also
on their callers, I would suggest to consider having
the handler check that it is, in fact, on a definiton and
not a declaration and issuing a warning that it has no effect
on a declaration, to avoid misunderstandings.

If either a new attribute or a new option is introduced they
need to be documented in the manual.  From your description
it's not clear to me if it's just about disabling the expansion
of the built-ins or if it's meant to prevent GCC from making
any assumptions about their semantics in calls to the annotated
functions.  That will likely be of interest to users and it
matters for diagnostics.

We will also need to add tests for the feature.  There are many
built-ins in GCC but the patch only affects a small subset of
them (e.g., I see it affects a few stdio and string functions
but I'm not sure the printf family are among them -- all those
handled in gimple-ssa-sprintf.c).

Besides its effect on codegen, I would also hope for a few test
cases for warnings, those issued by the strlen pass in particular,
to make sure their effects either are preserved or are disabled
(whichever is appropriate).

Martin

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



More information about the Gcc-patches mailing list