From d7b0fcaa39bc612b4b80fb1529d19ef3c1c842ea Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Thu, 24 Nov 2016 15:45:18 -0700 Subject: [PATCH] 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 --- gcc/ChangeLog | 11 ++ gcc/gimple-ssa-sprintf.c | 49 ++++++-- gcc/testsuite/ChangeLog | 5 + .../gcc.dg/tree-ssa/builtin-sprintf-5.c | 118 ++++++++++++++++++ 4 files changed, 170 insertions(+), 13 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-5.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 9e4c9b3b070..ab4ab36ea4c 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,13 @@ +2016-11-24 Martin Sebor + + 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 * 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 * common/config/sparc/sparc-common.c (sparc_option_optimization_table): diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c index ead8b0ed5a0..dc2b66d3430 100644 --- a/gcc/gimple-ssa-sprintf.c +++ b/gcc/gimple-ssa-sprintf.c @@ -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); } } diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 0994787853c..5e81768b4d0 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2016-11-24 Martin Sebor + + PR tree-optimization/78476 + * gcc.dg/tree-ssa/builtin-sprintf-5.c: New test. + 2016-11-24 Vladimir Makarov PR rtl-optimization/77541 diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-5.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-5.c new file mode 100644 index 00000000000..d568f9cbda3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-5.c @@ -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"} } */ -- 2.30.2