PR tree-optimization/78476 - snprintf(0, 0, ...) with known arguments not optimized away
gcc/testsuite/ChangeLog: PR tree-optimization/78476 * gcc.dg/tree-ssa/builtin-sprintf-5.c: New test. gcc/ChangeLog: PR tree-optimization/78476 * gimple-ssa-sprintf.c (struct pass_sprintf_length::call_info): Add a member. (handle_gimple_call): Adjust signature. (try_substitute_return_value): Remove calls to bounded functions with zero buffer size whose result is known. (pass_sprintf_length::execute): Adjust call to handle_gimple_call. From-SVN: r242854
This commit is contained in:
parent
6f8bb76372
commit
d7b0fcaa39
|
@ -1,3 +1,13 @@
|
|||
2016-11-24 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR tree-optimization/78476
|
||||
* gimple-ssa-sprintf.c (struct pass_sprintf_length::call_info):
|
||||
Add a member.
|
||||
(handle_gimple_call): Adjust signature.
|
||||
(try_substitute_return_value): Remove calls to bounded functions
|
||||
with zero buffer size whose result is known.
|
||||
(pass_sprintf_length::execute): Adjust call to handle_gimple_call.
|
||||
|
||||
2016-11-24 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE>
|
||||
|
||||
* varasm.c (assemble_start_function): Wrap align_log definition in
|
||||
|
@ -47,6 +57,7 @@
|
|||
PR target/67822
|
||||
* config/nvptx/mkoffload.c (main): Allow -fopenmp.
|
||||
|
||||
>>>>>>> .r242853
|
||||
2016-11-24 Eric Botcazou <ebotcazou@adacore.com>
|
||||
|
||||
* common/config/sparc/sparc-common.c (sparc_option_optimization_table):
|
||||
|
|
|
@ -62,6 +62,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "tree-object-size.h"
|
||||
#include "params.h"
|
||||
#include "tree-cfg.h"
|
||||
#include "tree-ssa-propagate.h"
|
||||
#include "calls.h"
|
||||
#include "cfgloop.h"
|
||||
#include "intl.h"
|
||||
|
@ -122,7 +123,7 @@ public:
|
|||
fold_return_value = param;
|
||||
}
|
||||
|
||||
void handle_gimple_call (gimple_stmt_iterator);
|
||||
void handle_gimple_call (gimple_stmt_iterator*);
|
||||
|
||||
struct call_info;
|
||||
void compute_format_length (const call_info &, format_result *);
|
||||
|
@ -712,6 +713,11 @@ struct pass_sprintf_length::call_info
|
|||
/* True for functions like snprintf that specify the size of
|
||||
the destination, false for others like sprintf that don't. */
|
||||
bool bounded;
|
||||
|
||||
/* True for bounded functions like snprintf that specify a zero-size
|
||||
buffer as a request to compute the size of output without actually
|
||||
writing any. */
|
||||
bool nowrite;
|
||||
};
|
||||
|
||||
/* Return the result of formatting the '%%' directive. */
|
||||
|
@ -2481,7 +2487,7 @@ get_destination_size (tree dest)
|
|||
have its range set to the range of return values, if that is known. */
|
||||
|
||||
static void
|
||||
try_substitute_return_value (gimple_stmt_iterator gsi,
|
||||
try_substitute_return_value (gimple_stmt_iterator *gsi,
|
||||
const pass_sprintf_length::call_info &info,
|
||||
const format_result &res)
|
||||
{
|
||||
|
@ -2497,14 +2503,29 @@ try_substitute_return_value (gimple_stmt_iterator gsi,
|
|||
&& (info.bounded || res.number_chars <= info.objsize)
|
||||
&& res.number_chars - 1 <= target_int_max ())
|
||||
{
|
||||
/* Replace the left-hand side of the call with the constant
|
||||
result of the formatted function minus 1 for the terminating
|
||||
NUL which the functions' return value does not include. */
|
||||
gimple_call_set_lhs (info.callstmt, NULL_TREE);
|
||||
tree cst = build_int_cst (integer_type_node, res.number_chars - 1);
|
||||
gimple *g = gimple_build_assign (lhs, cst);
|
||||
gsi_insert_after (&gsi, g, GSI_NEW_STMT);
|
||||
update_stmt (info.callstmt);
|
||||
|
||||
if (info.nowrite)
|
||||
{
|
||||
/* Replace the call to the bounded function with a zero size
|
||||
(e.g., snprintf(0, 0, "%i", 123) with the constant result
|
||||
of the function minus 1 for the terminating NUL which
|
||||
the function's return value does not include. */
|
||||
if (!update_call_from_tree (gsi, cst))
|
||||
gimplify_and_update_call_from_tree (gsi, cst);
|
||||
gimple *callstmt = gsi_stmt (*gsi);
|
||||
update_stmt (callstmt);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Replace the left-hand side of the call with the constant
|
||||
result of the formatted function minus 1 for the terminating
|
||||
NUL which the function's return value does not include. */
|
||||
gimple_call_set_lhs (info.callstmt, NULL_TREE);
|
||||
gimple *g = gimple_build_assign (lhs, cst);
|
||||
gsi_insert_after (gsi, g, GSI_NEW_STMT);
|
||||
update_stmt (info.callstmt);
|
||||
}
|
||||
|
||||
if (dump_file)
|
||||
{
|
||||
|
@ -2514,7 +2535,8 @@ try_substitute_return_value (gimple_stmt_iterator gsi,
|
|||
print_generic_expr (dump_file, cst, dump_flags);
|
||||
fprintf (dump_file, " for ");
|
||||
print_generic_expr (dump_file, info.func, dump_flags);
|
||||
fprintf (dump_file, " return value (output %s).\n",
|
||||
fprintf (dump_file, " %s (output %s).\n",
|
||||
info.nowrite ? "call" : "return value",
|
||||
res.constant ? "constant" : "variable");
|
||||
}
|
||||
}
|
||||
|
@ -2579,11 +2601,11 @@ try_substitute_return_value (gimple_stmt_iterator gsi,
|
|||
functions and if so, handle it. */
|
||||
|
||||
void
|
||||
pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator gsi)
|
||||
pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator *gsi)
|
||||
{
|
||||
call_info info = call_info ();
|
||||
|
||||
info.callstmt = gsi_stmt (gsi);
|
||||
info.callstmt = gsi_stmt (*gsi);
|
||||
if (!gimple_call_builtin_p (info.callstmt, BUILT_IN_NORMAL))
|
||||
return;
|
||||
|
||||
|
@ -2736,6 +2758,7 @@ pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator gsi)
|
|||
without actually producing any. Pretend the size is
|
||||
unlimited in this case. */
|
||||
info.objsize = HOST_WIDE_INT_MAX;
|
||||
info.nowrite = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2796,7 +2819,7 @@ pass_sprintf_length::execute (function *fun)
|
|||
gimple *stmt = gsi_stmt (si);
|
||||
|
||||
if (is_gimple_call (stmt))
|
||||
handle_gimple_call (si);
|
||||
handle_gimple_call (&si);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
2016-11-24 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR tree-optimization/78476
|
||||
* gcc.dg/tree-ssa/builtin-sprintf-5.c: New test.
|
||||
|
||||
2016-11-24 Vladimir Makarov <vmakarov@redhat.com>
|
||||
|
||||
PR rtl-optimization/77541
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
/* PR middle-end/78476 - snprintf(0, 0, ...) with known arguments not
|
||||
optimized away
|
||||
{ dg-compile }
|
||||
{ dg-options "-O2 -fdump-tree-optimized" }
|
||||
{ dg-require-effective-target int32plus } */
|
||||
|
||||
#define CAT(s, n) s ## n
|
||||
#define FAIL(line) CAT (failure_on_line_, line)
|
||||
#define PASS(line) CAT (success_on_line_, line)
|
||||
|
||||
/* Emit a call to a function named failure_on_line_NNN when EXPR is false. */
|
||||
#define ASSERT(value, expect) \
|
||||
do { \
|
||||
extern void FAIL (__LINE__)(int); \
|
||||
extern void PASS (__LINE__)(int); \
|
||||
if (value == expect) \
|
||||
PASS (__LINE__)(value); \
|
||||
else \
|
||||
FAIL (__LINE__)(value); \
|
||||
} while (0)
|
||||
|
||||
#define T(expect, ...) \
|
||||
do { \
|
||||
int n = __builtin_snprintf (0, 0, __VA_ARGS__); \
|
||||
ASSERT (n, expect); \
|
||||
} while (0)
|
||||
|
||||
int ival (int i) { return i; }
|
||||
|
||||
void test_arg_int (int i, int n)
|
||||
{
|
||||
T (1, "%i", ival (0));
|
||||
T (1, "%i", ival (1));
|
||||
T (2, "%i%i", ival (0), ival (1));
|
||||
T (3, "%i%i%i", ival (0), ival (1), ival (9));
|
||||
T (5, "%i %i %i", ival (0), ival (1), ival (9));
|
||||
|
||||
T (5, "%i %i %i", ival (0), ival (1), ival (9));
|
||||
|
||||
T (13, "%hhu.%hhu.%hhu.%hhu", ival (23), ival (78), ival (216), ival (135));
|
||||
|
||||
for (i = 0; i != 9; ++i)
|
||||
T (1, "%i", i);
|
||||
|
||||
for (i = -n; i != n; ++i)
|
||||
T (8, "%08x", i);
|
||||
}
|
||||
|
||||
void test_arg_string (const char *s)
|
||||
{
|
||||
T ( 0, "%-s", "");
|
||||
T ( 1, "%%");
|
||||
T ( 1, "%-s", "1");
|
||||
T ( 2, "%-s", "12");
|
||||
T ( 3, "%-s", "123");
|
||||
T ( 5, "s=%s", "123");
|
||||
T (10, "%%s=\"%s\"", "12345");
|
||||
|
||||
T ( 1, "%.*s", 1, "123");
|
||||
T ( 2, "%.*s", 2, "123");
|
||||
T ( 3, "%.*s", 3, "123");
|
||||
T ( 3, "%.*s", 4, "123");
|
||||
|
||||
T ( 1, "%1.*s", 1, "123");
|
||||
T ( 2, "%1.*s", 2, "123");
|
||||
T ( 3, "%1.*s", 3, "123");
|
||||
T ( 3, "%1.*s", 4, "123");
|
||||
T ( 4, "%4.*s", 1, "123");
|
||||
T ( 4, "%4.*s", 2, "123");
|
||||
T ( 4, "%4.*s", 3, "123");
|
||||
T ( 4, "%4.*s", 4, "123");
|
||||
T ( 4, "%4.*s", 5, "123");
|
||||
|
||||
const char *a = "123";
|
||||
const char *b = "456";
|
||||
|
||||
T ( 3, "%-s", s ? a : b);
|
||||
T ( 0, "%.0s", s);
|
||||
T ( 1, "%1.1s", s);
|
||||
T ( 2, "%2.2s", s);
|
||||
T ( 2, "%2.1s", s);
|
||||
}
|
||||
|
||||
void test_arg_multiarg (int i, double d)
|
||||
{
|
||||
T (16, "%i %f %s", 123, 3.14, "abc");
|
||||
T (16, "%12i %s", i, "abc");
|
||||
T (16, "%*i %s", 12, i, "abc");
|
||||
}
|
||||
|
||||
#define TV(expect, fmt, va) \
|
||||
do { \
|
||||
int n = __builtin_vsnprintf (0, 0, fmt, va); \
|
||||
ASSERT (n, expect); \
|
||||
} while (0)
|
||||
|
||||
void test_va_int (__builtin_va_list va)
|
||||
{
|
||||
TV ( 2, "%02hhx", va);
|
||||
TV ( 2, "%02.*hhx", va);
|
||||
TV ( 4, "%04hx", va);
|
||||
TV ( 4, "%04.*hx", va);
|
||||
}
|
||||
|
||||
void test_va_multiarg (__builtin_va_list va)
|
||||
{
|
||||
TV ( 8, "%8x", va);
|
||||
TV ( 8, "% 8x", va);
|
||||
TV ( 9, "%9x", va);
|
||||
TV (11, "%11o", va);
|
||||
TV (12, "%12o", va);
|
||||
|
||||
TV (16, "%12i %3.2s", va);
|
||||
}
|
||||
|
||||
|
||||
/* { dg-final { scan-tree-dump-not "failure_on_line" "optimized"} }
|
||||
{ dg-final { scan-tree-dump-not "snprintf" "optimized"} } */
|
Loading…
Reference in New Issue