This is the last patch, moving stpcpy folding. There are still string function foldings left but those are exclusively GENERIC now with no chance of advertedly recursing from GIMPLE folding via GENERIC folding back to GIMPLE folding. I'll deal with those during next stage1.
Bootstrapped on x86_64-unknown-linux-gnu, testing in progress. Richard. 2014-12-04 Richard Biener <rguent...@suse.de> * builtins.c (fold_builtin_0): Remove unused ignore parameter. (fold_builtin_1): Likewise. (fold_builtin_3): Likewise. (fold_builtin_varargs): Likewise. (fold_builtin_2): Likewise. Do not fold stpcpy here. (fold_builtin_n): Adjust. (fold_builtin_stpcpy): Move to gimple-fold.c. (gimple_fold_builtin_stpcpy): Moved and gimplified from builtins.c. (gimple_fold_builtin): Fold stpcpy here. Index: trunk/gcc/builtins.c =================================================================== *** trunk.orig/gcc/builtins.c 2014-12-04 09:44:01.583387589 +0100 --- trunk/gcc/builtins.c 2014-12-04 12:50:13.254000789 +0100 *************** static tree fold_builtin_fabs (location_ *** 191,201 **** static tree fold_builtin_abs (location_t, tree, tree); static tree fold_builtin_unordered_cmp (location_t, tree, tree, tree, enum tree_code, enum tree_code); ! static tree fold_builtin_0 (location_t, tree, bool); ! static tree fold_builtin_1 (location_t, tree, tree, bool); ! static tree fold_builtin_2 (location_t, tree, tree, tree, bool); ! static tree fold_builtin_3 (location_t, tree, tree, tree, tree, bool); ! static tree fold_builtin_varargs (location_t, tree, tree*, int, bool); static tree fold_builtin_strpbrk (location_t, tree, tree, tree); static tree fold_builtin_strstr (location_t, tree, tree, tree); --- 191,201 ---- static tree fold_builtin_abs (location_t, tree, tree); static tree fold_builtin_unordered_cmp (location_t, tree, tree, tree, enum tree_code, enum tree_code); ! static tree fold_builtin_0 (location_t, tree); ! static tree fold_builtin_1 (location_t, tree, tree); ! static tree fold_builtin_2 (location_t, tree, tree, tree); ! static tree fold_builtin_3 (location_t, tree, tree, tree, tree); ! static tree fold_builtin_varargs (location_t, tree, tree*, int); static tree fold_builtin_strpbrk (location_t, tree, tree, tree); static tree fold_builtin_strstr (location_t, tree, tree, tree); *************** fold_builtin_exponent (location_t loc, t *** 8657,8703 **** return NULL_TREE; } - /* Fold function call to builtin stpcpy with arguments DEST and SRC. - Return NULL_TREE if no simplification can be made. */ - - static tree - fold_builtin_stpcpy (location_t loc, tree fndecl, tree dest, tree src) - { - tree fn, len, lenp1, call, type; - - if (!validate_arg (dest, POINTER_TYPE) - || !validate_arg (src, POINTER_TYPE)) - return NULL_TREE; - - len = c_strlen (src, 1); - if (!len - || TREE_CODE (len) != INTEGER_CST) - return NULL_TREE; - - if (optimize_function_for_size_p (cfun) - /* If length is zero it's small enough. */ - && !integer_zerop (len)) - return NULL_TREE; - - fn = builtin_decl_implicit (BUILT_IN_MEMCPY); - if (!fn) - return NULL_TREE; - - lenp1 = size_binop_loc (loc, PLUS_EXPR, - fold_convert_loc (loc, size_type_node, len), - build_int_cst (size_type_node, 1)); - /* We use dest twice in building our expression. Save it from - multiple expansions. */ - dest = builtin_save_expr (dest); - call = build_call_expr_loc (loc, fn, 3, dest, src, lenp1); - - type = TREE_TYPE (TREE_TYPE (fndecl)); - dest = fold_build_pointer_plus_loc (loc, dest, len); - dest = fold_convert_loc (loc, type, dest); - dest = omit_one_operand_loc (loc, type, dest, call); - return dest; - } - /* Fold function call to builtin memchr. ARG1, ARG2 and LEN are the arguments to the call, and TYPE is its return type. Return NULL_TREE if no simplification can be made. */ --- 8657,8662 ---- *************** fold_builtin_arith_overflow (location_t *** 9857,9867 **** } /* Fold a call to built-in function FNDECL with 0 arguments. ! IGNORE is true if the result of the function call is ignored. This ! function returns NULL_TREE if no simplification was possible. */ static tree ! fold_builtin_0 (location_t loc, tree fndecl, bool ignore ATTRIBUTE_UNUSED) { tree type = TREE_TYPE (TREE_TYPE (fndecl)); enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); --- 9816,9825 ---- } /* Fold a call to built-in function FNDECL with 0 arguments. ! This function returns NULL_TREE if no simplification was possible. */ static tree ! fold_builtin_0 (location_t loc, tree fndecl) { tree type = TREE_TYPE (TREE_TYPE (fndecl)); enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); *************** fold_builtin_0 (location_t loc, tree fnd *** 9886,9896 **** } /* Fold a call to built-in function FNDECL with 1 argument, ARG0. ! IGNORE is true if the result of the function call is ignored. This ! function returns NULL_TREE if no simplification was possible. */ static tree ! fold_builtin_1 (location_t loc, tree fndecl, tree arg0, bool) { tree type = TREE_TYPE (TREE_TYPE (fndecl)); enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); --- 9844,9853 ---- } /* Fold a call to built-in function FNDECL with 1 argument, ARG0. ! This function returns NULL_TREE if no simplification was possible. */ static tree ! fold_builtin_1 (location_t loc, tree fndecl, tree arg0) { tree type = TREE_TYPE (TREE_TYPE (fndecl)); enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); *************** fold_builtin_1 (location_t loc, tree fnd *** 10301,10311 **** } /* Fold a call to built-in function FNDECL with 2 arguments, ARG0 and ARG1. ! IGNORE is true if the result of the function call is ignored. This ! function returns NULL_TREE if no simplification was possible. */ static tree ! fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1, bool ignore) { tree type = TREE_TYPE (TREE_TYPE (fndecl)); enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); --- 10258,10267 ---- } /* Fold a call to built-in function FNDECL with 2 arguments, ARG0 and ARG1. ! This function returns NULL_TREE if no simplification was possible. */ static tree ! fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1) { tree type = TREE_TYPE (TREE_TYPE (fndecl)); enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); *************** fold_builtin_2 (location_t loc, tree fnd *** 10392,10410 **** case BUILT_IN_RINDEX: return fold_builtin_strrchr (loc, arg0, arg1, type); - case BUILT_IN_STPCPY: - if (ignore) - { - tree fn = builtin_decl_implicit (BUILT_IN_STRCPY); - if (!fn) - break; - - return build_call_expr_loc (loc, fn, 2, arg0, arg1); - } - else - return fold_builtin_stpcpy (loc, fndecl, arg0, arg1); - break; - case BUILT_IN_STRCMP: return fold_builtin_strcmp (loc, arg0, arg1); --- 10348,10353 ---- *************** fold_builtin_2 (location_t loc, tree fnd *** 10469,10480 **** } /* Fold a call to built-in function FNDECL with 3 arguments, ARG0, ARG1, ! and ARG2. IGNORE is true if the result of the function call is ignored. This function returns NULL_TREE if no simplification was possible. */ static tree fold_builtin_3 (location_t loc, tree fndecl, ! tree arg0, tree arg1, tree arg2, bool) { tree type = TREE_TYPE (TREE_TYPE (fndecl)); enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); --- 10412,10423 ---- } /* Fold a call to built-in function FNDECL with 3 arguments, ARG0, ARG1, ! and ARG2. This function returns NULL_TREE if no simplification was possible. */ static tree fold_builtin_3 (location_t loc, tree fndecl, ! tree arg0, tree arg1, tree arg2) { tree type = TREE_TYPE (TREE_TYPE (fndecl)); enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); *************** fold_builtin_3 (location_t loc, tree fnd *** 10543,10568 **** simplification was possible. */ tree ! fold_builtin_n (location_t loc, tree fndecl, tree *args, int nargs, bool ignore) { tree ret = NULL_TREE; switch (nargs) { case 0: ! ret = fold_builtin_0 (loc, fndecl, ignore); break; case 1: ! ret = fold_builtin_1 (loc, fndecl, args[0], ignore); break; case 2: ! ret = fold_builtin_2 (loc, fndecl, args[0], args[1], ignore); break; case 3: ! ret = fold_builtin_3 (loc, fndecl, args[0], args[1], args[2], ignore); break; default: ! ret = fold_builtin_varargs (loc, fndecl, args, nargs, ignore); break; } if (ret) --- 10486,10511 ---- simplification was possible. */ tree ! fold_builtin_n (location_t loc, tree fndecl, tree *args, int nargs, bool) { tree ret = NULL_TREE; switch (nargs) { case 0: ! ret = fold_builtin_0 (loc, fndecl); break; case 1: ! ret = fold_builtin_1 (loc, fndecl, args[0]); break; case 2: ! ret = fold_builtin_2 (loc, fndecl, args[0], args[1]); break; case 3: ! ret = fold_builtin_3 (loc, fndecl, args[0], args[1], args[2]); break; default: ! ret = fold_builtin_varargs (loc, fndecl, args, nargs); break; } if (ret) *************** fold_builtin_object_size (tree ptr, tree *** 11656,11667 **** need special handling; we need to store the arguments in a convenient data structure before attempting any folding. Fortunately there are only a few builtins that fall into this category. FNDECL is the ! function, EXP is the CALL_EXPR for the call, and IGNORE is true if the ! result of the function call is ignored. */ static tree ! fold_builtin_varargs (location_t loc, tree fndecl, tree *args, int nargs, ! bool ignore ATTRIBUTE_UNUSED) { enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); tree ret = NULL_TREE; --- 11599,11608 ---- need special handling; we need to store the arguments in a convenient data structure before attempting any folding. Fortunately there are only a few builtins that fall into this category. FNDECL is the ! function, EXP is the CALL_EXPR for the call. */ static tree ! fold_builtin_varargs (location_t loc, tree fndecl, tree *args, int nargs) { enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); tree ret = NULL_TREE; Index: trunk/gcc/gimple-fold.c =================================================================== *** trunk.orig/gcc/gimple-fold.c 2014-12-04 09:44:03.083387537 +0100 --- trunk/gcc/gimple-fold.c 2014-12-04 13:08:23.796963031 +0100 *************** gimple_fold_builtin_stxncpy_chk (gimple_ *** 2052,2057 **** --- 2052,2118 ---- return true; } + /* Fold function call to builtin stpcpy with arguments DEST and SRC. + Return NULL_TREE if no simplification can be made. */ + + static bool + gimple_fold_builtin_stpcpy (gimple_stmt_iterator *gsi) + { + gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi)); + location_t loc = gimple_location (stmt); + tree dest = gimple_call_arg (stmt, 0); + tree src = gimple_call_arg (stmt, 1); + tree fn, len, lenp1; + + /* If the result is unused, replace stpcpy with strcpy. */ + if (gimple_call_lhs (stmt) == NULL_TREE) + { + tree fn = builtin_decl_implicit (BUILT_IN_STRCPY); + if (!fn) + return false; + gimple_call_set_fndecl (stmt, fn); + fold_stmt (gsi); + return true; + } + + len = c_strlen (src, 1); + if (!len + || TREE_CODE (len) != INTEGER_CST) + return false; + + if (optimize_function_for_size_p (cfun) + /* If length is zero it's small enough. */ + && !integer_zerop (len)) + return false; + + /* If the source has a known length replace stpcpy with memcpy. */ + fn = builtin_decl_implicit (BUILT_IN_MEMCPY); + if (!fn) + return false; + + gimple_seq stmts = NULL; + len = gimple_convert (&stmts, loc, size_type_node, len); + lenp1 = gimple_build (&stmts, loc, PLUS_EXPR, size_type_node, + len, build_int_cst (size_type_node, 1)); + gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT); + gcall *repl = gimple_build_call (fn, 3, dest, src, lenp1); + gimple_set_vuse (repl, gimple_vuse (stmt)); + gimple_set_vdef (repl, gimple_vdef (stmt)); + if (gimple_vdef (repl) + && TREE_CODE (gimple_vdef (repl)) == SSA_NAME) + SSA_NAME_DEF_STMT (gimple_vdef (repl)) = repl; + gsi_insert_before (gsi, repl, GSI_SAME_STMT); + /* Replace the result with dest + lenp1. */ + gassign *ret = gimple_build_assign (gimple_call_lhs (stmt), + POINTER_PLUS_EXPR, dest, lenp1); + gsi_replace (gsi, ret, true); + /* Finally fold the memcpy call. */ + gimple_stmt_iterator gsi2 = *gsi; + gsi_prev (&gsi2); + fold_stmt (&gsi2); + return true; + } + /* Fold a call EXP to {,v}snprintf having NARGS passed as ARGS. Return NULL_TREE if a normal call should be emitted rather than expanding the function inline. FCODE is either BUILT_IN_SNPRINTF_CHK or *************** gimple_fold_builtin (gimple_stmt_iterato *** 2849,2854 **** --- 2910,2917 ---- gimple_call_arg (stmt, 2), gimple_call_arg (stmt, 3), fcode); + case BUILT_IN_STPCPY: + return gimple_fold_builtin_stpcpy (gsi); case BUILT_IN_STRCPY_CHK: case BUILT_IN_STPCPY_CHK: return gimple_fold_builtin_stxcpy_chk (gsi,