From patchwork Tue Nov 2 21:14:58 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Keith Packard X-Patchwork-Id: 46970 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id DA7863858015 for ; Tue, 2 Nov 2021 21:16:09 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org DA7863858015 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1635887769; bh=RCGCVwY6jFLj3uc6Udl7nh6w35MCNQkBQ8Y3EAqd+ZM=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=G3H365VgQrrZHiXd5qt6rrad15/llrAt+nwov7RmPa66F+Z2/qri62BN4eIn217nv Odh6sHJP0cODrO1gJGHcgLtXvKcQnSvza8QKms7e/avx+p7ntrhsqBZhKhfZbI9iy/ c68ddWbdlq0mpoZ8THXeYLwpSgwbgD+5D4QRevpg= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from elaine.keithp.com (home.keithp.com [63.227.221.253]) by sourceware.org (Postfix) with ESMTPS id B98803858C27; Tue, 2 Nov 2021 21:15:04 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org B98803858C27 Received: from localhost (localhost [127.0.0.1]) by elaine.keithp.com (Postfix) with ESMTP id 6D73C3F310D5; Tue, 2 Nov 2021 14:15:03 -0700 (PDT) X-Virus-Scanned: Debian amavisd-new at keithp.com Received: from elaine.keithp.com ([127.0.0.1]) by localhost (elaine.keithp.com [127.0.0.1]) (amavisd-new, port 10024) with LMTP id YYoazMzPcMZ9; Tue, 2 Nov 2021 14:15:00 -0700 (PDT) Received: from keithp.com (koto.keithp.com [192.168.11.2]) by elaine.keithp.com (Postfix) with ESMTPSA id AC5CA3F310C2; Tue, 2 Nov 2021 14:15:00 -0700 (PDT) Received: by keithp.com (Postfix, from userid 1000) id 6F5871E6013C; Tue, 2 Nov 2021 14:15:00 -0700 (PDT) To: gcc@gcc.gnu.org Subject: [PATCH] Add 'no_builtin' function attribute Date: Tue, 2 Nov 2021 14:14:58 -0700 Message-Id: <20211102211458.10233-1-keithp@keithp.com> X-Mailer: git-send-email 2.33.1 In-Reply-To: <87r1by1qxg.fsf@keithp.com> References: <87r1by1qxg.fsf@keithp.com> MIME-Version: 1.0 X-Spam-Status: No, score=-10.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Keith Packard via Gcc-patches From: Keith Packard Reply-To: Keith Packard Cc: gcc-patches@gcc.gnu.org Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" 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 --- 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 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 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