Commit ba3ceb2d by Jakub Jelinek Committed by Jakub Jelinek

re PR middle-end/47917 (snprintf(..., "const string") should be optimized into memcpy)

	PR middle-end/47917
	* builtins.c (fold_builtin_snprintf): New function.
	(fold_builtin_3): Call it for BUILT_IN_SNPRINTF.
	(fold_builtin_4): Likewise.

	* gcc.c-torture/execute/pr47917.c: New test.
	* gcc.dg/pr47917.c: New test.

From-SVN: r170959
parent d63c53cc
2011-03-14 Jakub Jelinek <jakub@redhat.com>
PR middle-end/47917
* builtins.c (fold_builtin_snprintf): New function.
(fold_builtin_3): Call it for BUILT_IN_SNPRINTF.
(fold_builtin_4): Likewise.
PR middle-end/38878
* fold-const.c (fold_binary_loc) <case EQ_EXPR, NE_EXPR>: Add
STRIP_NOPS on arg0 and arg1. When optimizing X +- C == X
......
/* Expand builtin functions.
Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
Free Software Foundation, Inc.
This file is part of GCC.
......@@ -191,6 +191,7 @@ 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);
static rtx expand_builtin_memory_chk (tree, rtx, enum machine_mode,
......@@ -10596,6 +10597,9 @@ fold_builtin_3 (location_t loc, tree fndecl,
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);
case BUILT_IN_STRCPY_CHK:
case BUILT_IN_STPCPY_CHK:
return fold_builtin_stxcpy_chk (loc, fndecl, arg0, arg1, arg2, NULL_TREE,
......@@ -10661,6 +10665,9 @@ fold_builtin_4 (location_t loc, tree fndecl,
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)
......@@ -11919,6 +11926,127 @@ fold_builtin_sprintf (location_t loc, tree dest, tree fmt,
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.
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 (!host_integerp (destsize, 1))
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_low_cst (destsize, 1);
/* If the format doesn't contain % args or %%, use strcpy. */
if (strchr (fmt_str, target_percent) == NULL)
{
tree fn = implicit_built_in_decls[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 (NULL_TREE, 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 = implicit_built_in_decls[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 || !host_integerp (retval, 1))
return NULL_TREE;
origlen = tree_low_cst (retval, 1);
/* 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)
{
retval = fold_convert_loc
(loc, TREE_TYPE (TREE_TYPE (implicit_built_in_decls[BUILT_IN_SNPRINTF])),
retval);
return build2 (COMPOUND_EXPR, TREE_TYPE (retval), call, retval);
}
else
return call;
}
/* Expand a call EXP to __builtin_object_size. */
rtx
......
2011-03-14 Jakub Jelinek <jakub@redhat.com>
PR middle-end/47917
* gcc.c-torture/execute/pr47917.c: New test.
* gcc.dg/pr47917.c: New test.
PR middle-end/38878
* gcc.dg/tree-ssa/foldaddr-1.c: Remove xfail.
......
/* PR middle-end/47917 */
extern int snprintf (char *, __SIZE_TYPE__, const char *, ...);
extern int memcmp (const void *, const void *, __SIZE_TYPE__);
extern void abort (void);
char buf1[6], buf2[6], buf3[4], buf4[4];
int i;
int
foo (void)
{
int ret = snprintf (buf1, sizeof buf1, "abcde");
ret += snprintf (buf2, sizeof buf2, "abcdef") * 16;
ret += snprintf (buf3, sizeof buf3, "%s", i++ < 6 ? "abc" : "def") * 256;
ret += snprintf (buf4, sizeof buf4, "%s", i++ > 10 ? "abcde" : "defgh") * 4096;
return ret;
}
int
main (void)
{
if (foo () != 5 + 6 * 16 + 3 * 256 + 5 * 4096)
abort ();
if (memcmp (buf1, "abcde", 6) != 0
|| memcmp (buf2, "abcde", 6) != 0
|| memcmp (buf3, "abc", 4) != 0
|| memcmp (buf4, "def", 4) != 0
|| i != 2)
abort ();
return 0;
}
/* PR middle-end/47917 */
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-optimized" } */
extern int snprintf (char *, __SIZE_TYPE__, const char *, ...);
extern int memcmp (const void *, const void *, __SIZE_TYPE__);
extern void abort (void);
char buf1[6], buf2[6], buf3[4], buf4[4];
int i;
int
foo (void)
{
int ret = snprintf (buf1, sizeof buf1, "abcde");
ret += snprintf (buf2, sizeof buf2, "abcdef") * 16;
ret += snprintf (buf3, sizeof buf3, "%s", i++ < 6 ? "abc" : "def") * 256;
ret += snprintf (buf4, sizeof buf4, "%s", i++ > 10 ? "abcde" : "defgh") * 4096;
return ret;
}
int
main (void)
{
if (foo () != 5 + 6 * 16 + 3 * 256 + 5 * 4096)
abort ();
if (memcmp (buf1, "abcde", 6) != 0
|| memcmp (buf2, "abcde", 6) != 0
|| memcmp (buf3, "abc", 4) != 0
|| memcmp (buf4, "def", 4) != 0
|| i != 2)
abort ();
return 0;
}
/* { dg-final { scan-tree-dump-times "snprintf" 2 "optimized" } } */
/* { dg-final { scan-tree-dump-times "sprintf" 0 "optimized" } } */
/* { dg-final { cleanup-tree-dump "optimized" } } */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment