On Thu, 4 Dec 2014, Richard Biener wrote: > > 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.
The following is what I have applied. Bootstrapped / tested on x86_64-unknown-linux-gnu. Richard. 2014-12-08 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 14:24:58.265803955 +0100 --- trunk/gcc/builtins.c 2014-12-04 14:49:45.367752467 +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 14:24:58.265803955 +0100 --- trunk/gcc/gimple-fold.c 2014-12-04 14:50:19.438751288 +0100 *************** gimple_fold_builtin_stxncpy_chk (gimple_ *** 2052,2057 **** --- 2052,2121 ---- 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; + tree tem = gimple_convert (&stmts, loc, size_type_node, len); + lenp1 = gimple_build (&stmts, loc, PLUS_EXPR, size_type_node, + tem, 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 + len. */ + stmts = NULL; + tem = gimple_convert (&stmts, loc, sizetype, len); + gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT); + gassign *ret = gimple_build_assign (gimple_call_lhs (stmt), + POINTER_PLUS_EXPR, dest, tem); + 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 **** --- 2913,2920 ---- 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,