The following moves snprintf folding from builtins.c to gimple-fold.c. Bootstrapped and tested on x86_64-unknown-linux-gnu, applied.
Richard. 2014-08-18 Richard Biener <rguent...@suse.de> PR tree-optimization/62090 * builtins.c (fold_builtin_snprintf): Move to gimple-fold.c. (fold_builtin_3): Do not fold snprintf. (fold_builtin_4): Likewise. * gimple-fold.c (gimple_fold_builtin_snprintf): New function moved from builtins.c. (gimple_fold_builtin_with_strlen): Fold snprintf and sprintf. (gimple_fold_builtin): Do not fold sprintf here. * gcc.dg/pr62090-2.c: New testcase. Index: gcc/builtins.c =================================================================== *** gcc/builtins.c.orig 2014-08-18 14:33:16.783737621 +0200 --- gcc/builtins.c 2014-08-18 14:33:28.678736802 +0200 *************** static tree fold_builtin_strrchr (locati *** 190,196 **** 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_snprintf (location_t, tree, tree, tree, tree, int); static rtx expand_builtin_object_size (tree); static rtx expand_builtin_memory_chk (tree, rtx, enum machine_mode, --- 190,195 ---- *************** fold_builtin_3 (location_t loc, tree fnd *** 10309,10317 **** case BUILT_IN_MEMCMP: return fold_builtin_memcmp (loc, arg0, arg1, arg2);; - case BUILT_IN_SNPRINTF: - return fold_builtin_snprintf (loc, arg0, arg1, arg2, NULL_TREE, ignore); - case BUILT_IN_STRCAT_CHK: return fold_builtin_strcat_chk (loc, fndecl, arg0, arg1, arg2); --- 10308,10313 ---- *************** fold_builtin_4 (location_t loc, tree fnd *** 10364,10372 **** case BUILT_IN_STRNCAT_CHK: return fold_builtin_strncat_chk (loc, fndecl, arg0, arg1, arg2, arg3); - case BUILT_IN_SNPRINTF: - return fold_builtin_snprintf (loc, arg0, arg1, arg2, arg3, ignore); - case BUILT_IN_FPRINTF_CHK: case BUILT_IN_VFPRINTF_CHK: if (!validate_arg (arg1, INTEGER_TYPE) --- 10360,10365 ---- *************** fold_builtin_next_arg (tree exp, bool va *** 11230,11355 **** } - /* 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. - - 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_snprintf (location_t loc, tree dest, tree destsize, tree fmt, - tree orig, int ignored) - { - tree call, retval; - const char *fmt_str = NULL; - unsigned HOST_WIDE_INT destlen; - - /* Verify the required arguments in the original call. We deal with two - types of snprintf() calls: 'snprintf (str, cst, fmt)' and - 'snprintf (dest, cst, "%s", orig)'. */ - if (!validate_arg (dest, POINTER_TYPE) - || !validate_arg (destsize, INTEGER_TYPE) - || !validate_arg (fmt, POINTER_TYPE)) - return NULL_TREE; - if (orig && !validate_arg (orig, POINTER_TYPE)) - return NULL_TREE; - - if (!tree_fits_uhwi_p (destsize)) - 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; - - destlen = tree_to_uhwi (destsize); - - /* 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); - size_t len = strlen (fmt_str); - - /* Don't optimize snprintf (buf, 4, "abc", ptr++). */ - if (orig) - return NULL_TREE; - - /* We could expand this as - memcpy (str, fmt, cst - 1); str[cst - 1] = '\0'; - or to - memcpy (str, fmt_with_nul_at_cstm1, cst); - but in the former case that might increase code size - and in the latter case grow .rodata section too much. - So punt for now. */ - if (len >= destlen) - return NULL_TREE; - - if (!fn) - return NULL_TREE; - - /* Convert snprintf (str, cst, fmt) into strcpy (str, fmt) when - 'format' is known to contain no % formats and - strlen (fmt) < cst. */ - 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 = builtin_decl_implicit (BUILT_IN_STRCPY); - unsigned HOST_WIDE_INT origlen; - - /* Don't crash on snprintf (str1, cst, "%s"). */ - if (!orig) - return NULL_TREE; - - retval = c_strlen (orig, 1); - if (!retval || !tree_fits_uhwi_p (retval)) - return NULL_TREE; - - origlen = tree_to_uhwi (retval); - /* We could expand this as - memcpy (str1, str2, cst - 1); str1[cst - 1] = '\0'; - or to - memcpy (str1, str2_with_nul_at_cstm1, cst); - but in the former case that might increase code size - and in the latter case grow .rodata section too much. - So punt for now. */ - if (origlen >= destlen) - return NULL_TREE; - - /* Convert snprintf (str1, cst, "%s", str2) into - strcpy (str1, str2) if strlen (str2) < cst. */ - if (!fn) - return NULL_TREE; - - call = build_call_expr_loc (loc, fn, 2, dest, orig); - - if (ignored) - retval = NULL_TREE; - } - - if (call && retval) - { - tree fn = builtin_decl_explicit (BUILT_IN_SNPRINTF); - retval = fold_convert_loc (loc, TREE_TYPE (TREE_TYPE (fn)), retval); - return build2 (COMPOUND_EXPR, TREE_TYPE (retval), call, retval); - } - else - return call; - } - /* Expand a call EXP to __builtin_object_size. */ static rtx --- 11223,11228 ---- Index: gcc/gimple-fold.c =================================================================== *** gcc/gimple-fold.c.orig 2014-08-18 14:33:16.802737620 +0200 --- gcc/gimple-fold.c 2014-08-18 14:43:12.832696584 +0200 *************** gimple_fold_builtin_sprintf_chk (gimple_ *** 2091,2097 **** 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); --- 2091,2097 ---- the caller does not use the returned value of the function. */ static bool ! gimple_fold_builtin_sprintf (gimple_stmt_iterator *gsi, tree orig_len) { gimple stmt = gsi_stmt (*gsi); tree dest = gimple_call_arg (stmt, 0); *************** gimple_fold_builtin_sprintf (gimple_stmt *** 2169,2179 **** if (!orig) return false; ! tree len = NULL_TREE; ! if (gimple_call_lhs (stmt)) { ! len = c_strlen (orig, 1); ! if (!len) return false; } --- 2169,2179 ---- if (!orig) return false; ! if (gimple_call_lhs (stmt) ! && !orig_len) { ! orig_len = c_strlen (orig, 1); ! if (!orig_len) return false; } *************** gimple_fold_builtin_sprintf (gimple_stmt *** 2183,2191 **** 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 --- 2183,2192 ---- gimple_seq_add_stmt_without_update (&stmts, repl); if (gimple_call_lhs (stmt)) { ! if (!useless_type_conversion_p (integer_type_node, ! TREE_TYPE (orig_len))) ! orig_len = fold_convert (integer_type_node, orig_len); ! repl = gimple_build_assign (gimple_call_lhs (stmt), orig_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 *************** gimple_fold_builtin_sprintf (gimple_stmt *** 2206,2212 **** --- 2207,2353 ---- return false; } + /* 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. + + 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_snprintf (gimple_stmt_iterator *gsi, tree orig_len) + { + gimple stmt = gsi_stmt (*gsi); + tree dest = gimple_call_arg (stmt, 0); + tree destsize = gimple_call_arg (stmt, 1); + tree fmt = gimple_call_arg (stmt, 2); + tree orig = NULL_TREE; + const char *fmt_str = NULL; + + if (gimple_call_num_args (stmt) > 4) + return false; + + if (gimple_call_num_args (stmt) == 4) + orig = gimple_call_arg (stmt, 3); + + if (!tree_fits_uhwi_p (destsize)) + return false; + unsigned HOST_WIDE_INT destlen = tree_to_uhwi (destsize); + + /* 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 snprintf (buf, 4, "abc", ptr++). */ + if (orig) + return false; + + /* We could expand this as + memcpy (str, fmt, cst - 1); str[cst - 1] = '\0'; + or to + memcpy (str, fmt_with_nul_at_cstm1, cst); + but in the former case that might increase code size + and in the latter case grow .rodata section too much. + So punt for now. */ + size_t len = strlen (fmt_str); + if (len >= destlen) + return false; + + 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, 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; + } + + /* 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); + if (!fn) + return false; + + /* Don't crash on snprintf (str1, cst, "%s"). */ + if (!orig) + return false; + + if (!orig_len) + { + orig_len = c_strlen (orig, 1); + if (!orig_len) + return false; + } + + /* We could expand this as + memcpy (str1, str2, cst - 1); str1[cst - 1] = '\0'; + or to + memcpy (str1, str2_with_nul_at_cstm1, cst); + but in the former case that might increase code size + and in the latter case grow .rodata section too much. + So punt for now. */ + if (compare_tree_int (orig_len, destlen) >= 0) + return false; + + /* Convert snprintf (str1, cst, "%s", str2) into + strcpy (str1, str2) if strlen (str2) < cst. */ + 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 (orig_len))) + orig_len = fold_convert (integer_type_node, orig_len); + repl = gimple_build_assign (gimple_call_lhs (stmt), orig_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. */ *************** static bool *** 2232,2238 **** gimple_fold_builtin_with_strlen (gimple_stmt_iterator *gsi) { gimple stmt = gsi_stmt (*gsi); ! tree val[3]; tree a; int arg_idx, type; bitmap visited; --- 2373,2379 ---- gimple_fold_builtin_with_strlen (gimple_stmt_iterator *gsi) { gimple stmt = gsi_stmt (*gsi); ! tree val[4]; tree a; int arg_idx, type; bitmap visited; *************** gimple_fold_builtin_with_strlen (gimple_ *** 2276,2298 **** arg_idx = 1; type = 2; break; default: return false; } int nargs = gimple_call_num_args (stmt); - if (arg_idx >= nargs) - return false; /* Try to use the dataflow information gathered by the CCP process. */ visited = BITMAP_ALLOC (NULL); bitmap_clear (visited); memset (val, 0, sizeof (val)); ! a = gimple_call_arg (stmt, arg_idx); ! if (!get_maxval_strlen (a, &val[arg_idx], visited, type) ! || !is_gimple_val (val[arg_idx])) ! val[arg_idx] = NULL_TREE; BITMAP_FREE (visited); --- 2417,2448 ---- arg_idx = 1; type = 2; break; + case BUILT_IN_SPRINTF: + arg_idx = 2; + type = 0; + break; + case BUILT_IN_SNPRINTF: + arg_idx = 3; + type = 0; + break; default: return false; } int nargs = gimple_call_num_args (stmt); /* Try to use the dataflow information gathered by the CCP process. */ visited = BITMAP_ALLOC (NULL); bitmap_clear (visited); memset (val, 0, sizeof (val)); ! if (arg_idx < nargs) ! { ! a = gimple_call_arg (stmt, arg_idx); ! if (!get_maxval_strlen (a, &val[arg_idx], visited, type) ! || !is_gimple_val (val[arg_idx])) ! val[arg_idx] = NULL_TREE; ! } BITMAP_FREE (visited); *************** gimple_fold_builtin_with_strlen (gimple_ *** 2364,2370 **** case BUILT_IN_VSNPRINTF_CHK: return gimple_fold_builtin_snprintf_chk (gsi, val[1], DECL_FUNCTION_CODE (callee)); ! default: gcc_unreachable (); } --- 2514,2523 ---- case BUILT_IN_VSNPRINTF_CHK: return gimple_fold_builtin_snprintf_chk (gsi, val[1], DECL_FUNCTION_CODE (callee)); ! case BUILT_IN_SNPRINTF: ! return gimple_fold_builtin_snprintf (gsi, val[3]); ! case BUILT_IN_SPRINTF: ! return gimple_fold_builtin_sprintf (gsi, val[2]); default: gcc_unreachable (); } *************** gimple_fold_builtin (gimple_stmt_iterato *** 2414,2421 **** 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:; } --- 2567,2572 ---- Index: gcc/testsuite/gcc.dg/pr62090-2.c =================================================================== *** /dev/null 1970-01-01 00:00:00.000000000 +0000 --- gcc/testsuite/gcc.dg/pr62090-2.c 2014-08-18 14:33:28.689736801 +0200 *************** *** 0 **** --- 1,24 ---- + /* { dg-do compile } */ + /* { dg-options "-O2" } */ + + typedef long unsigned int size_t; + extern __inline __attribute__ ((__always_inline__)) + __attribute__ ((__gnu_inline__)) int + snprintf (char *__restrict __s, size_t __n, const char *__restrict __fmt, ...) + { + return __builtin___snprintf_chk (__s, __n, 2 - 1, + __builtin_object_size (__s, 2 > 1), + __fmt, __builtin_va_arg_pack ()); + } + typedef struct apacket apacket; + struct apacket { + unsigned char data[4096]; + }; + static size_t fill_connect_data(char *buf, size_t bufsize) + { + return snprintf(buf, bufsize, "host::") + 1; + } + unsigned send_connect(apacket *cp) + { + return fill_connect_data((char *)cp->data, sizeof(cp->data)); + }