This fixes PR64313 - the match-and-simplify machinery has a hard time re-doing the "magic" trick of re-using builtins in the matched IL and thus fails to generate calls to builtins that are not marked as builtin_decl_implicit_p.
The following patch fixes this by remembering whether the user has declared a builtin in a new per-builtin flag and recognizing explicit uses (in ADDR_EXPRs which catches calls and address-takens) during gimplification. For explicitely used builtins the compiler can generate them implicitely (well, not 100% true - the semantics do not have to agree unless the identifier is reserved, sth we can't check easily). This is an approach that follows that of the STPCPY case right next to the place this patch patches the frontends, just a little bit less aggressive in requiring an explicit use of the builtin. No attempt is done to preserve (and merge) this through LTO - builtins are streamed by simply streaming DECL_FUNCTION_CODE which makes doing this a bit hard(er). The gcc.dg/torture/builtin-explog-1.c testcase was failing on bare-metal targets and is fixed with the patch below already during early optimizations. An alternative to fix the regression is to remove the very few builtin simplification patterns from match.pd and/or restore the folding code in builtins.c. The issue would then need to be revisited during the next stage1 (where I can look into how to deal with this with LTO - but 'implicit_p' is also not transitioned properly if you combine for example a C89 input and C99 input - lto1 defaults all of flag_isoc{94,99,11} to zero and uses the C family builtins.def way of initialization, so the LTO issue is certainly pre-existing). Ok for trunk now? Bootstrap and regtest is running on x86_64-unknown-linux-gnu. Thanks, Richard. 2015-01-19 Richard Biener <rguent...@suse.de> PR middle-end/64313 * tree-core.h (builtin_info, builtin_info_type): Turn from an object with two arrays into an array of an object with decl and two flags, implicit_p and declared_p. * tree.h (builtin_decl_explicit, builtin_decl_implicit, set_builtin_decl, set_builtin_decl_implicit_p, builtin_decl_explicit_p, builtin_decl_implicit_p): Adjust. (set_builtin_decl_declared_p, builtin_decl_declared_p): New functions. * builtins.c (builtin_info): Adjust. * gimplify.c (gimplify_addr_expr): References to builtins that have been declared by the user makes them eligible for use by the compiler. Call set_builtin_decl_implicit_p on them. c/ * c-decl.c (merge_decls): Call set_builtin_decl_declared_p for builtins the user declared correctly. cp/ * decl.c (duplicate_decls): Call set_builtin_decl_declared_p for builtins the user declared correctly. Index: gcc/tree.h =================================================================== *** gcc/tree.h.orig 2015-01-19 14:51:26.175142267 +0100 --- gcc/tree.h 2015-01-19 15:11:48.602099942 +0100 *************** builtin_decl_explicit (enum built_in_fun *** 4606,4612 **** { gcc_checking_assert (BUILTIN_VALID_P (fncode)); ! return builtin_info.decl[(size_t)fncode]; } /* Return the tree node for an implicit builtin function or NULL. */ --- 4606,4612 ---- { gcc_checking_assert (BUILTIN_VALID_P (fncode)); ! return builtin_info[(size_t)fncode].decl; } /* Return the tree node for an implicit builtin function or NULL. */ *************** builtin_decl_implicit (enum built_in_fun *** 4616,4625 **** size_t uns_fncode = (size_t)fncode; gcc_checking_assert (BUILTIN_VALID_P (fncode)); ! if (!builtin_info.implicit_p[uns_fncode]) return NULL_TREE; ! return builtin_info.decl[uns_fncode]; } /* Set explicit builtin function nodes and whether it is an implicit --- 4616,4625 ---- size_t uns_fncode = (size_t)fncode; gcc_checking_assert (BUILTIN_VALID_P (fncode)); ! if (!builtin_info[uns_fncode].implicit_p) return NULL_TREE; ! return builtin_info[uns_fncode].decl; } /* Set explicit builtin function nodes and whether it is an implicit *************** set_builtin_decl (enum built_in_function *** 4633,4640 **** gcc_checking_assert (BUILTIN_VALID_P (fncode) && (decl != NULL_TREE || !implicit_p)); ! builtin_info.decl[ufncode] = decl; ! builtin_info.implicit_p[ufncode] = implicit_p; } /* Set the implicit flag for a builtin function. */ --- 4633,4641 ---- gcc_checking_assert (BUILTIN_VALID_P (fncode) && (decl != NULL_TREE || !implicit_p)); ! builtin_info[ufncode].decl = decl; ! builtin_info[ufncode].implicit_p = implicit_p; ! builtin_info[ufncode].declared_p = false; } /* Set the implicit flag for a builtin function. */ *************** set_builtin_decl_implicit_p (enum built_ *** 4645,4653 **** size_t uns_fncode = (size_t)fncode; gcc_checking_assert (BUILTIN_VALID_P (fncode) ! && builtin_info.decl[uns_fncode] != NULL_TREE); ! builtin_info.implicit_p[uns_fncode] = implicit_p; } /* Return whether the standard builtin function can be used as an explicit --- 4646,4667 ---- size_t uns_fncode = (size_t)fncode; gcc_checking_assert (BUILTIN_VALID_P (fncode) ! && builtin_info[uns_fncode].decl != NULL_TREE); ! builtin_info[uns_fncode].implicit_p = implicit_p; ! } ! ! /* Set the declared flag for a builtin function. */ ! ! static inline void ! set_builtin_decl_declared_p (enum built_in_function fncode, bool declared_p) ! { ! size_t uns_fncode = (size_t)fncode; ! ! gcc_checking_assert (BUILTIN_VALID_P (fncode) ! && builtin_info[uns_fncode].decl != NULL_TREE); ! ! builtin_info[uns_fncode].declared_p = declared_p; } /* Return whether the standard builtin function can be used as an explicit *************** static inline bool *** 4657,4663 **** builtin_decl_explicit_p (enum built_in_function fncode) { gcc_checking_assert (BUILTIN_VALID_P (fncode)); ! return (builtin_info.decl[(size_t)fncode] != NULL_TREE); } /* Return whether the standard builtin function can be used implicitly. */ --- 4671,4677 ---- builtin_decl_explicit_p (enum built_in_function fncode) { gcc_checking_assert (BUILTIN_VALID_P (fncode)); ! return (builtin_info[(size_t)fncode].decl != NULL_TREE); } /* Return whether the standard builtin function can be used implicitly. */ *************** builtin_decl_implicit_p (enum built_in_f *** 4668,4675 **** size_t uns_fncode = (size_t)fncode; gcc_checking_assert (BUILTIN_VALID_P (fncode)); ! return (builtin_info.decl[uns_fncode] != NULL_TREE ! && builtin_info.implicit_p[uns_fncode]); } /* Return true if T (assumed to be a DECL) is a global variable. --- 4682,4701 ---- size_t uns_fncode = (size_t)fncode; gcc_checking_assert (BUILTIN_VALID_P (fncode)); ! return (builtin_info[uns_fncode].decl != NULL_TREE ! && builtin_info[uns_fncode].implicit_p); ! } ! ! /* Return whether the standard builtin function was declared. */ ! ! static inline bool ! builtin_decl_declared_p (enum built_in_function fncode) ! { ! size_t uns_fncode = (size_t)fncode; ! ! gcc_checking_assert (BUILTIN_VALID_P (fncode)); ! return (builtin_info[uns_fncode].decl != NULL_TREE ! && builtin_info[uns_fncode].declared_p); } /* Return true if T (assumed to be a DECL) is a global variable. Index: gcc/tree-core.h =================================================================== *** gcc/tree-core.h.orig 2015-01-19 14:51:26.175142267 +0100 --- gcc/tree-core.h 2015-01-19 15:10:01.111103664 +0100 *************** struct const_call_expr_arg_iterator { *** 1853,1863 **** }; /* The builtin_info structure holds the FUNCTION_DECL of the standard builtin ! function, and a flag that says if the function is available implicitly, or ! whether the user has to code explicit calls to __builtin_<xxx>. */ struct GTY(()) builtin_info_type { ! tree decl[(int)END_BUILTINS]; ! bool implicit_p[(int)END_BUILTINS]; }; --- 1853,1866 ---- }; /* The builtin_info structure holds the FUNCTION_DECL of the standard builtin ! function, and flags. */ struct GTY(()) builtin_info_type { ! tree decl; ! /* Whether the user can use <xxx> instead of explicitely using calls ! to __builtin_<xxx>. */ ! unsigned implicit_p : 1; ! /* Whether the user has provided a declaration of <xxx>. */ ! unsigned declared_p : 1; }; *************** extern int tree_node_sizes[]; *** 1913,1919 **** extern bool in_gimple_form; /* Functional interface to the builtin functions. */ ! extern GTY(()) builtin_info_type builtin_info; /* If nonzero, an upper limit on alignment of structure fields, in bits, */ extern unsigned int maximum_field_alignment; --- 1916,1922 ---- extern bool in_gimple_form; /* Functional interface to the builtin functions. */ ! extern GTY(()) builtin_info_type builtin_info[(int)END_BUILTINS]; /* If nonzero, an upper limit on alignment of structure fields, in bits, */ extern unsigned int maximum_field_alignment; Index: gcc/c/c-decl.c =================================================================== *** gcc/c/c-decl.c.orig 2015-01-19 14:51:40.378141775 +0100 --- gcc/c/c-decl.c 2015-01-19 14:51:46.891141549 +0100 *************** merge_decls (tree newdecl, tree olddecl, *** 2582,2587 **** --- 2582,2589 ---- set_builtin_decl_implicit_p (fncode, true); break; default: + if (builtin_decl_explicit_p (fncode)) + set_builtin_decl_declared_p (fncode, true); break; } } Index: gcc/cp/decl.c =================================================================== *** gcc/cp/decl.c.orig 2015-01-16 09:40:55.145761684 +0100 --- gcc/cp/decl.c 2015-01-19 14:58:13.496128164 +0100 *************** duplicate_decls (tree newdecl, tree oldd *** 2309,2314 **** --- 2309,2316 ---- set_builtin_decl_implicit_p (fncode, true); break; default: + if (builtin_decl_explicit_p (fncode)) + set_builtin_decl_declared_p (fncode, true); break; } } Index: gcc/gimplify.c =================================================================== *** gcc/gimplify.c.orig 2015-01-16 09:40:55.904761658 +0100 --- gcc/gimplify.c 2015-01-19 14:56:10.403132426 +0100 *************** gimplify_addr_expr (tree *expr_p, gimple *** 4956,4961 **** --- 4956,4969 ---- break; default: + /* If we see a call to a declared builtin or see its address + being taken (we can unify those cases here) then we can mark + the builtin for implicit generation by GCC. */ + if (TREE_CODE (op0) == FUNCTION_DECL + && DECL_BUILT_IN_CLASS (op0) == BUILT_IN_NORMAL + && builtin_decl_declared_p (DECL_FUNCTION_CODE (op0))) + set_builtin_decl_implicit_p (DECL_FUNCTION_CODE (op0), true); + /* We use fb_either here because the C frontend sometimes takes the address of a call that returns a struct; see gcc.dg/c99-array-lval-1.c. The gimplifier will correctly make Index: gcc/builtins.c =================================================================== *** gcc/builtins.c.orig 2015-01-16 14:13:07.679196199 +0100 --- gcc/builtins.c 2015-01-19 15:12:39.633098175 +0100 *************** const char * built_in_names[(int) END_BU *** 105,113 **** }; #undef DEF_BUILTIN ! /* Setup an array of _DECL trees, make sure each element is initialized to NULL_TREE. */ ! builtin_info_type builtin_info; /* Non-zero if __builtin_constant_p should be folded right away. */ bool force_folding_builtin_constant_p; --- 105,113 ---- }; #undef DEF_BUILTIN ! /* Setup an array of builtin_info_type, make sure each element decl is initialized to NULL_TREE. */ ! builtin_info_type builtin_info[(int)END_BUILTINS]; /* Non-zero if __builtin_constant_p should be folded right away. */ bool force_folding_builtin_constant_p;