From patchwork Wed Nov 23 03:02:31 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Sebor X-Patchwork-Id: 83522 Delivered-To: patch@linaro.org Received: by 10.140.97.165 with SMTP id m34csp2431780qge; Tue, 22 Nov 2016 19:02:54 -0800 (PST) X-Received: by 10.84.142.1 with SMTP id 1mr1936994plw.87.1479870174610; Tue, 22 Nov 2016 19:02:54 -0800 (PST) Return-Path: Received: from sourceware.org (server1.sourceware.org. [209.132.180.131]) by mx.google.com with ESMTPS id 1si2774586plv.49.2016.11.22.19.02.54 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 22 Nov 2016 19:02:54 -0800 (PST) Received-SPF: pass (google.com: domain of gcc-patches-return-442304-patch=linaro.org@gcc.gnu.org designates 209.132.180.131 as permitted sender) client-ip=209.132.180.131; Authentication-Results: mx.google.com; dkim=pass header.i=@gcc.gnu.org; spf=pass (google.com: domain of gcc-patches-return-442304-patch=linaro.org@gcc.gnu.org designates 209.132.180.131 as permitted sender) smtp.mailfrom=gcc-patches-return-442304-patch=linaro.org@gcc.gnu.org; dmarc=fail (p=NONE dis=NONE) header.from=gmail.com DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:to :from:subject:message-id:date:mime-version:content-type; q=dns; s=default; b=aaguUCe1pnxwI4HD+yuCLRAt8bZtWfYgyp/BFJa8lwb1tNC3lM Sm+x4SCBpiTSbQbH93yvGou2MysMZobWWIn7eCYZBUA/ZZmadj/5wSK+ziVY3cW1 U6AhecK89WzVYKfhwaIdnQqGQrqx0ECYVXBpD9YrNf7NfBADw3vKF3RU0= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:to :from:subject:message-id:date:mime-version:content-type; s= default; bh=N90tuD4zf7xwY2axzhXVkpCEepo=; b=CzCA/JiG5hRInvuXAvA8 qD0CDsv/xdug1UCf1FVsZ5yZ1H4FFtdWKoVnRjZG4AQ03K1Xxxq+3DOhUXvd/yqr 58SMtWZ2NflvLWULeLZuwikfi8J8dXAwE7SP5iEUmRTp4Jx9HQlpA7CFn3mj63tG L5hNbKuknK0ewAq0kTDIbQs= Received: (qmail 125281 invoked by alias); 23 Nov 2016 03:02:38 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 125268 invoked by uid 89); 23 Nov 2016 03:02:38 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.8 required=5.0 tests=AWL, BAYES_00, FREEMAIL_FROM, KAM_ASCII_DIVIDERS, RCVD_IN_DNSWL_LOW, SPF_PASS autolearn=no version=3.3.2 spammy=22s, ival, int32plus, target_int_max X-HELO: mail-qt0-f179.google.com Received: from mail-qt0-f179.google.com (HELO mail-qt0-f179.google.com) (209.85.216.179) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 23 Nov 2016 03:02:36 +0000 Received: by mail-qt0-f179.google.com with SMTP id n6so488297qtd.1 for ; Tue, 22 Nov 2016 19:02:35 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:to:from:subject:message-id:date:user-agent :mime-version; bh=mA/Sum1BLcX2+p21M9VxK1dtVHUDvO6PK3IqDK/v4HI=; b=R5CQgW3zbQR/fHOdYqmLZNiVaGhmiCKO9ZRzwkbhAnDorauwups1QNh0mfYi6xIc3N yRT+3GplmMJcsYo7WtA/RZilOUQBUPonQL6jk2jMA+v00CoSPP1Y2Hqu2+/ypFGCUyPY hQn7+tZhEAPFvjznjAsYRaqFbMBIKSqiRF0vTlF5Q4pgHc2Bn9Y3476zqEqamvt70HoJ 4ZQuvQu7UG41ZU5Wf+Sd/d3na3DTSquiC6opUWnsva5UI/zONRe7grK7A/s7iZ4XnMd/ kJIiC3ll5Rp6mXPf7OK6X7M9tBygE5yp1j/GR+dRSGjxEl5ccjscz8S4S5WPBVCpj4oF u43Q== X-Gm-Message-State: AKaTC0114vEOfzR82xrGBkTX8g3Tk07Dd1Bgm0iIYsQGypdRQW5XhuMadF3//f4o+W59xg== X-Received: by 10.200.58.65 with SMTP id w59mr714856qte.54.1479870154342; Tue, 22 Nov 2016 19:02:34 -0800 (PST) Received: from [192.168.0.26] (75-166-206-79.hlrn.qwest.net. [75.166.206.79]) by smtp.gmail.com with ESMTPSA id x123sm15201620qkx.46.2016.11.22.19.02.33 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 22 Nov 2016 19:02:33 -0800 (PST) To: Gcc Patch List From: Martin Sebor Subject: [PATCH] eliminate calls to snprintf(0, 0, ...) with known return value (pr78476) Message-ID: Date: Tue, 22 Nov 2016 20:02:31 -0700 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.3.0 MIME-Version: 1.0 X-IsSubscribed: yes Calls to bounded functions like snprintf with a zero-size buffer are special requests to compute the size of output without actually writing any. For example: int n = snprintf(0, 0, "%08x", rand ()); is a request to compute the number of bytes that the function would format if it were passed a buffer of non-zero size. In the example above since the return value is known to be exactly 8, not only can the snprintf return value be folded into a constant but the whole call to snprintf can be eliminated. The attached patch enables this optimization under the -fprintf-return-value option. The patch depends on the one for bug 78461 (posted earlier today) during the testing of which I noticed that this optimization was missing from the gimple-ssa-sprintf pass. Thanks Martin 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. Index: gcc/gimple-ssa-sprintf.c =================================================================== --- gcc/gimple-ssa-sprintf.c (revision 242703) +++ gcc/gimple-ssa-sprintf.c (working copy) @@ -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 @@ class pass_sprintf_length : public gimple_opt_pass 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. */ @@ -2484,7 +2490,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) { @@ -2500,15 +2506,30 @@ static void && (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) { location_t callloc = gimple_location (info.callstmt); @@ -2517,7 +2538,8 @@ static void 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"); } } @@ -2582,11 +2604,11 @@ static void 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; @@ -2739,6 +2761,7 @@ void without actually producing any. Pretend the size is unlimited in this case. */ info.objsize = HOST_WIDE_INT_MAX; + info.nowrite = true; } else { @@ -2799,7 +2822,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); } } Index: gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-5.c =================================================================== --- gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-5.c (revision 0) +++ gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-5.c (working copy) @@ -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"} } */