diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 7ec321cddf8..00f58677cf0 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,13 @@ +2014-08-14 Richard Biener + + PR tree-optimization/62090 + * builtins.c (fold_builtin_sprintf): Move to gimple-fold.c. + (fold_builtin_2): Do not fold sprintf. + (fold_builtin_3): Likewise. + * gimple-fold.c (gimple_fold_builtin_sprintf): New function + moved from builtins.c. + (gimple_fold_builtin): Fold sprintf. + 2014-08-14 Richard Biener PR rtl-optimization/62079 diff --git a/gcc/builtins.c b/gcc/builtins.c index c1bd2634716..84685689739 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -190,7 +190,6 @@ static tree fold_builtin_strrchr (location_t, tree, tree, tree); static tree fold_builtin_strncat (location_t, tree, tree, tree); static tree fold_builtin_strspn (location_t, tree, tree); static tree fold_builtin_strcspn (location_t, tree, tree); -static tree fold_builtin_sprintf (location_t, tree, tree, tree, int); static tree fold_builtin_snprintf (location_t, tree, tree, tree, tree, int); static rtx expand_builtin_object_size (tree); @@ -10234,9 +10233,6 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1, bool ignore) case BUILT_IN_VA_START: break; - case BUILT_IN_SPRINTF: - return fold_builtin_sprintf (loc, arg0, arg1, NULL_TREE, ignore); - case BUILT_IN_OBJECT_SIZE: return fold_builtin_object_size (arg0, arg1); @@ -10313,9 +10309,6 @@ fold_builtin_3 (location_t loc, tree fndecl, case BUILT_IN_MEMCMP: return fold_builtin_memcmp (loc, arg0, arg1, arg2);; - case BUILT_IN_SPRINTF: - return fold_builtin_sprintf (loc, arg0, arg1, arg2, ignore); - case BUILT_IN_SNPRINTF: return fold_builtin_snprintf (loc, arg0, arg1, arg2, NULL_TREE, ignore); @@ -11237,94 +11230,6 @@ fold_builtin_next_arg (tree exp, bool va_start_p) } -/* Simplify a call to the sprintf builtin with arguments DEST, FMT, and ORIG. - ORIG may be null if this is a 2-argument call. We don't attempt to - simplify calls with more than 3 arguments. - - Return NULL_TREE if no simplification was possible, otherwise return the - simplified form of the call as a tree. If IGNORED is true, it means that - the caller does not use the returned value of the function. */ - -static tree -fold_builtin_sprintf (location_t loc, tree dest, tree fmt, - tree orig, int ignored) -{ - tree call, retval; - const char *fmt_str = NULL; - - /* Verify the required arguments in the original call. We deal with two - types of sprintf() calls: 'sprintf (str, fmt)' and - 'sprintf (dest, "%s", orig)'. */ - if (!validate_arg (dest, POINTER_TYPE) - || !validate_arg (fmt, POINTER_TYPE)) - return NULL_TREE; - if (orig && !validate_arg (orig, POINTER_TYPE)) - return NULL_TREE; - - /* Check whether the format is a literal string constant. */ - fmt_str = c_getstr (fmt); - if (fmt_str == NULL) - return NULL_TREE; - - call = NULL_TREE; - retval = NULL_TREE; - - if (!init_target_chars ()) - return NULL_TREE; - - /* 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); - - if (!fn) - return NULL_TREE; - - /* Don't optimize sprintf (buf, "abc", ptr++). */ - if (orig) - return NULL_TREE; - - /* Convert sprintf (str, fmt) into strcpy (str, fmt) when - 'format' is known to contain no % formats. */ - call = build_call_expr_loc (loc, fn, 2, dest, fmt); - if (!ignored) - retval = build_int_cst (integer_type_node, strlen (fmt_str)); - } - - /* 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; - fn = builtin_decl_implicit (BUILT_IN_STRCPY); - - if (!fn) - return NULL_TREE; - - /* Don't crash on sprintf (str1, "%s"). */ - if (!orig) - return NULL_TREE; - - /* Convert sprintf (str1, "%s", str2) into strcpy (str1, str2). */ - if (!ignored) - { - retval = c_strlen (orig, 1); - if (!retval || TREE_CODE (retval) != INTEGER_CST) - return NULL_TREE; - } - call = build_call_expr_loc (loc, fn, 2, dest, orig); - } - - if (call && retval) - { - retval = fold_convert_loc - (loc, TREE_TYPE (TREE_TYPE (builtin_decl_implicit (BUILT_IN_SPRINTF))), - retval); - return build2 (COMPOUND_EXPR, TREE_TYPE (retval), call, retval); - } - else - return call; -} - /* Simplify a call to the snprintf builtin with arguments DEST, DESTSIZE, FMT, and ORIG. ORIG may be null if this is a 3-argument call. We don't attempt to simplify calls with more than 4 arguments. diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index 20c7cb04a3b..4fa1a3579c3 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -2143,6 +2143,132 @@ gimple_fold_builtin_sprintf_chk (gimple_stmt_iterator *gsi, return true; } +/* Simplify a call to the sprintf builtin with arguments DEST, FMT, and ORIG. + ORIG may be null if this is a 2-argument call. We don't attempt to + simplify calls with more than 3 arguments. + + Return NULL_TREE if no simplification was possible, otherwise return the + simplified form of the call as a tree. If IGNORED is true, it means that + the caller does not use the returned value of the function. */ + +static bool +gimple_fold_builtin_sprintf (gimple_stmt_iterator *gsi) +{ + gimple stmt = gsi_stmt (*gsi); + tree dest = gimple_call_arg (stmt, 0); + tree fmt = gimple_call_arg (stmt, 1); + tree orig = NULL_TREE; + const char *fmt_str = NULL; + + /* Verify the required arguments in the original call. We deal with two + types of sprintf() calls: 'sprintf (str, fmt)' and + 'sprintf (dest, "%s", orig)'. */ + if (gimple_call_num_args (stmt) > 3) + return false; + + if (gimple_call_num_args (stmt) == 3) + orig = gimple_call_arg (stmt, 2); + + /* Check whether the format is a literal string constant. */ + fmt_str = c_getstr (fmt); + if (fmt_str == NULL) + return false; + + if (!init_target_chars ()) + return false; + + /* 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); + + if (!fn) + return false; + + /* Don't optimize sprintf (buf, "abc", ptr++). */ + if (orig) + return false; + + /* Convert sprintf (str, fmt) into strcpy (str, fmt) when + 'format' is known to contain no % formats. */ + gimple_seq stmts = NULL; + gimple repl = gimple_build_call (fn, 2, dest, fmt); + gimple_seq_add_stmt_without_update (&stmts, repl); + if (gimple_call_lhs (stmt)) + { + repl = gimple_build_assign (gimple_call_lhs (stmt), + build_int_cst (integer_type_node, + strlen (fmt_str))); + gimple_seq_add_stmt_without_update (&stmts, repl); + gsi_replace_with_seq_vops (gsi, stmts); + /* gsi now points at the assignment to the lhs, get a + stmt iterator to the memcpy call. + ??? We can't use gsi_for_stmt as that doesn't work when the + CFG isn't built yet. */ + gimple_stmt_iterator gsi2 = *gsi; + gsi_prev (&gsi2); + fold_stmt (&gsi2); + } + else + { + gsi_replace_with_seq_vops (gsi, stmts); + fold_stmt (gsi); + } + return true; + } + + /* 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; + fn = builtin_decl_implicit (BUILT_IN_STRCPY); + + if (!fn) + return false; + + /* Don't crash on sprintf (str1, "%s"). */ + if (!orig) + return false; + + tree len = NULL_TREE; + if (gimple_call_lhs (stmt)) + { + len = c_strlen (orig, 1); + if (!len) + return false; + } + + /* Convert sprintf (str1, "%s", str2) into strcpy (str1, str2). */ + gimple_seq stmts = NULL; + gimple repl = gimple_build_call (fn, 2, dest, orig); + gimple_seq_add_stmt_without_update (&stmts, repl); + if (gimple_call_lhs (stmt)) + { + if (!useless_type_conversion_p (integer_type_node, TREE_TYPE (len))) + len = fold_convert (integer_type_node, len); + repl = gimple_build_assign (gimple_call_lhs (stmt), len); + gimple_seq_add_stmt_without_update (&stmts, repl); + gsi_replace_with_seq_vops (gsi, stmts); + /* gsi now points at the assignment to the lhs, get a + stmt iterator to the memcpy call. + ??? We can't use gsi_for_stmt as that doesn't work when the + CFG isn't built yet. */ + gimple_stmt_iterator gsi2 = *gsi; + gsi_prev (&gsi2); + fold_stmt (&gsi2); + } + else + { + gsi_replace_with_seq_vops (gsi, stmts); + fold_stmt (gsi); + } + return true; + } + return false; +} + + + /* Fold a call to __builtin_strlen with known length LEN. */ @@ -2349,6 +2475,8 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi) case BUILT_IN_SPRINTF_CHK: case BUILT_IN_VSPRINTF_CHK: return gimple_fold_builtin_sprintf_chk (gsi, DECL_FUNCTION_CODE (callee)); + case BUILT_IN_SPRINTF: + return gimple_fold_builtin_sprintf (gsi); default:; } diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 0dd986b4c96..17e75e128b2 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2014-08-14 Richard Biener + + PR tree-optimization/62090 + * gcc.dg/pr62090.c: New testcase. + 2014-08-14 Richard Biener PR rtl-optimization/62079 diff --git a/gcc/testsuite/gcc.dg/pr62090.c b/gcc/testsuite/gcc.dg/pr62090.c new file mode 100644 index 00000000000..53089cf1932 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr62090.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +long a; +int *b; +extern __inline __attribute__ ((__always_inline__)) +__attribute__ ((__gnu_inline__)) int sprintf (int *p1, char *p2, ...) +{ + a = __builtin_object_size (0, 0); + return __builtin___sprintf_chk (0, 0, a, p2, __builtin_va_arg_pack ()); +} + +void +log_bad_request () +{ + b += sprintf (0, "foo"); +}