Hi! As mentioned in PR78901, the current -fprintf-return-value optimization will remove (replace with constant) calls like: int foo (void) { return snprintf (0, 0, "%s%d", "abcde", 5); } but not void bar (void) { snprintf (0, 0, "%s%d", "abcde", 5); } which doesn't have any side-effect either (and there we even don't care that much about whether the return value is constant or not, just that there are no other side-effects like %n directive).
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2017-01-02 Jakub Jelinek <ja...@redhat.com> * gimple-ssa-sprintf.c (try_substitute_return_value): Remove info.nowrite calls with no lhs that can't throw. Return bool whether gsi_remove has been called or not. (pass_sprintf_length::handle_gimple_call): Return bool whether try_substitute_return_value called gsi_remove. Formatting fix. (pass_sprintf_length::execute): Don't use gsi_remove if handle_gimple_call returned true. * gcc.dg/tree-ssa/builtin-snprintf-1.c: New test. --- gcc/gimple-ssa-sprintf.c.jj 2017-01-02 13:05:08.400755201 +0100 +++ gcc/gimple-ssa-sprintf.c 2017-01-02 13:20:20.684895563 +0100 @@ -128,7 +128,7 @@ public: fold_return_value = param; } - void handle_gimple_call (gimple_stmt_iterator*); + bool handle_gimple_call (gimple_stmt_iterator *); struct call_info; bool compute_format_length (call_info &, format_result *); @@ -2686,7 +2686,7 @@ get_destination_size (tree dest) is known and exact. A result that isn't suitable for substitution may have its range set to the range of return values, if that is known. */ -static void +static bool try_substitute_return_value (gimple_stmt_iterator *gsi, const pass_sprintf_length::call_info &info, const format_result &res) @@ -2746,6 +2746,23 @@ try_substitute_return_value (gimple_stmt res.constant ? "constant" : "variable"); } } + else if (lhs == NULL_TREE + && info.nowrite + && !stmt_ends_bb_p (info.callstmt)) + { + /* Remove the call to the bounded function with a zero size + (e.g., snprintf(0, 0, "%i", 123)) if there is no lhs. */ + unlink_stmt_vdef (info.callstmt); + gsi_remove (gsi, true); + if (dump_file) + { + location_t callloc = gimple_location (info.callstmt); + fprintf (dump_file, "On line %i removing ", + LOCATION_LINE (callloc)); + print_generic_expr (dump_file, info.func, dump_flags); + fprintf (dump_file, " call.\n"); + } + } else { unsigned HOST_WIDE_INT maxbytes; @@ -2801,19 +2818,22 @@ try_substitute_return_value (gimple_stmt inbounds, (unsigned long)res.number_chars, ign); } } + + return false; } /* Determine if a GIMPLE CALL is to one of the sprintf-like built-in - functions and if so, handle it. */ + functions and if so, handle it. Return true if the call is removed + and gsi_next should not be performed in the caller. */ -void +bool pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator *gsi) { call_info info = call_info (); info.callstmt = gsi_stmt (*gsi); if (!gimple_call_builtin_p (info.callstmt, BUILT_IN_NORMAL)) - return; + return false; info.func = gimple_call_fndecl (info.callstmt); info.fncode = DECL_FUNCTION_CODE (info.func); @@ -2904,7 +2924,7 @@ pass_sprintf_length::handle_gimple_call break; default: - return; + return false; } /* The first argument is a pointer to the destination. */ @@ -2969,11 +2989,9 @@ pass_sprintf_length::handle_gimple_call } if (idx_objsize != HOST_WIDE_INT_M1U) - { - if (tree size = gimple_call_arg (info.callstmt, idx_objsize)) - if (tree_fits_uhwi_p (size)) - objsize = tree_to_uhwi (size); - } + if (tree size = gimple_call_arg (info.callstmt, idx_objsize)) + if (tree_fits_uhwi_p (size)) + objsize = tree_to_uhwi (size); if (info.bounded && !dstsize) { @@ -2998,7 +3016,7 @@ pass_sprintf_length::handle_gimple_call location_t loc = gimple_location (info.callstmt); warning_at (EXPR_LOC_OR_LOC (dstptr, loc), OPT_Wformat_length_, "null destination pointer"); - return; + return false; } /* Set the object size to the smaller of the two arguments @@ -3027,12 +3045,12 @@ pass_sprintf_length::handle_gimple_call location_t loc = gimple_location (info.callstmt); warning_at (EXPR_LOC_OR_LOC (info.format, loc), OPT_Wformat_length_, "null format string"); - return; + return false; } info.fmtstr = get_format_string (info.format, &info.fmtloc); if (!info.fmtstr) - return; + return false; /* The result is the number of bytes output by the formatted function, including the terminating NUL. */ @@ -3048,7 +3066,8 @@ pass_sprintf_length::handle_gimple_call && optimize > 0 && flag_printf_return_value && (!flag_rounding_math || !res.floating)) - try_substitute_return_value (gsi, info, res); + return try_substitute_return_value (gsi, info, res); + return false; } /* Execute the pass for function FUN. */ @@ -3059,14 +3078,14 @@ pass_sprintf_length::execute (function * basic_block bb; FOR_EACH_BB_FN (bb, fun) { - for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si); - gsi_next (&si)) + for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si); ) { /* Iterate over statements, looking for function calls. */ gimple *stmt = gsi_stmt (si); - if (is_gimple_call (stmt)) - handle_gimple_call (&si); + if (is_gimple_call (stmt) && handle_gimple_call (&si)) + continue; + gsi_next (&si); } } --- gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-1.c.jj 2017-01-02 13:13:09.401499255 +0100 +++ gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-1.c 2017-01-02 13:14:33.000000000 +0100 @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fprintf-return-value -fdump-tree-optimized" } */ +/* { dg-final { scan-tree-dump-not "__builtin_snprintf" "optimized"} } */ + +int +foo (void) +{ + int a = __builtin_snprintf (0, 0, "%s", "abcdefgh"); + return a; +} + +void +bar (void) +{ + __builtin_snprintf (0, 0, "%s", "abcdefgh"); +} Jakub