From patchwork Tue Mar 14 00:33:21 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Sebor X-Patchwork-Id: 738498 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3vhwhR0p8sz9s0g for ; Tue, 14 Mar 2017 11:33:42 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="dx2rasgr"; dkim-atps=neutral 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=eNR2xq+BAKH3KlzF4cZGBhiMIfPd5+2tqpGeZf6ohyAyWrDWNv yNvlsfnQGJBCAahCU5u3zLvijSVfh50UUDlm2+59LD1PXydJ1glnmGTB9wQvyMOm Gs+S0OjL6UhAON81r3gW+vDlhiMYM8Tg0CTCEmDXNY9qI6mzyBb6R/vjU= 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=yNMj0j7P4YluOOif6m5/ddXgKtg=; b=dx2rasgr55LvVJL/vZi1 FLrVImQUD621HDYXEgTR6TYzmXo/RzX37kx4+FA3xKvSBt7TS1irrMarK85K3Xsf FajU5m//CIimtwG4L2JBkIEke8NhwMxZW3sJQaNihBK+dVzlwX3FCo5embWoVQem NJwXqR96du2+iPyyWugLg+U= Received: (qmail 97652 invoked by alias); 14 Mar 2017 00:33:31 -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 97635 invoked by uid 89); 14 Mar 2017 00:33:30 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-25.7 required=5.0 tests=AWL, BAYES_00, FREEMAIL_FROM, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.2 spammy=asterisk, accounts X-HELO: mail-wr0-f170.google.com Received: from mail-wr0-f170.google.com (HELO mail-wr0-f170.google.com) (209.85.128.170) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Tue, 14 Mar 2017 00:33:28 +0000 Received: by mail-wr0-f170.google.com with SMTP id u48so114211635wrc.0 for ; Mon, 13 Mar 2017 17:33:28 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:to:from:subject:message-id:date:user-agent :mime-version; bh=1RwX9F0q9R/hjkW0EU/+N4YhSOV7579Ve29PKARJQUE=; b=HohdV64K2PRBu+IuBMIOimKidAoKC8YKEFGx84vK+MpPOFLn5r1jDO67n3d5wOD2cL N4b3IK9ia/fFXDYAHMDSNPrwFWV6NZJ7+MbH0Apeinkfy/AFnU6X9JvajW77RV144w9A m3KoF7zGkXkPnjUtYqbLsjftdmWPd1FBt6YYkIRjBeiC7f2wSZxeeOcIIT7aj46Mwe9F edd0WG9stbIIJipdVb3oJL+LdXwHxztJ7CojIE9lYJwtxQW3CBM7Ko+L9g3sQBm+cnz9 lO+yn1bT8gLAQJTHqyJXqcHBo+YVaYJFxZz04NjyRoiqY2CKcdE7ysnlj4/PasiMs58X wYwA== X-Gm-Message-State: AMke39mmIgvw4h6XyyzQ76IvgMNAbZt378V5SszcmzJ+kahnkC5LEvKVJF0ceo7DLWScfg== X-Received: by 10.223.151.198 with SMTP id t6mr30591016wrb.9.1489451606882; Mon, 13 Mar 2017 17:33:26 -0700 (PDT) Received: from localhost.localdomain (97-118-178-40.hlrn.qwest.net. [97.118.178.40]) by smtp.gmail.com with ESMTPSA id 198sm13270908wmn.11.2017.03.13.17.33.25 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 13 Mar 2017 17:33:26 -0700 (PDT) To: Gcc Patch List From: Martin Sebor Subject: [PATCH] correct handling of negative-positive precision ranges with floating directives (PR 79800) Message-ID: <10b80f30-8fd3-bc2c-0a09-64583dac88a7@gmail.com> Date: Mon, 13 Mar 2017 18:33:21 -0600 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.7.0 MIME-Version: 1.0 X-IsSubscribed: yes The output of a floating point directive whose precision is specified by an asterisk with an argument that's in a range that includes both negative and positive values less than six may include between zero and six fractional digits plus a decimal point. For example, printf ("%.*e", p, x); results in the 14 bytes -1.797693e+308 when p == -1 and x == -DBL_MIN because a negative precision is ignored and the directive assumes the default 6, and in just the one byte 0 when p == 0 and x == 0.0. Current trunk doesn't handle this case correctly when the floating argument isn't known and uses the upper bound of the specified precision as the maximum number of fractional digits. As a result, it sets the range on the return value in this case as [5, 7] (plus 5 for the longest multibyte decimal decimal point) when the correct range is [5, 14] as explained above (plus 5 again). The attached patch corrects the handling of such precision ranges to avoid this unlikely bug. Martin PR tree-optimization/79800 - wrong snprintf result range with precision in a narrow negative-positive range gcc/ChangeLog: PR tree-optimization/79800 * gimple-ssa-sprintf.c (format_floating: Add argument. Handle precision in negative-positive range. (format_floating): Call non-const overload with adjusted precision. gcc/testsuite/ChangeLog: PR tree-optimization/79800 * gcc.dg/tree-ssa/builtin-sprintf-warn-15.c: Add test cases. * gcc.dg/tree-ssa/pr79800.c: New test. diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c index 0448b21..2474391 100644 --- a/gcc/gimple-ssa-sprintf.c +++ b/gcc/gimple-ssa-sprintf.c @@ -1499,11 +1499,13 @@ format_floating_max (tree type, char spec, HOST_WIDE_INT prec) } /* Return a range representing the minimum and maximum number of bytes - that the directive DIR will output for any argument. This function - is used when the directive argument or its value isn't known. */ + that the directive DIR will output for any argument. PREC gives + the adjusted precision range to account for negative precisions + meaning the default 6. This function is used when the directive + argument or its value isn't known. */ static fmtresult -format_floating (const directive &dir) +format_floating (const directive &dir, const HOST_WIDE_INT prec[2]) { tree type; @@ -1532,8 +1534,8 @@ format_floating (const directive &dir) /* The minimum output as determined by flags. It's always at least 1. When plus or space are set the output is preceded by either a sign or a space. */ - int flagmin = (1 /* for the first digit */ - + (dir.get_flag ('+') | dir.get_flag (' '))); + unsigned flagmin = (1 /* for the first digit */ + + (dir.get_flag ('+') | dir.get_flag (' '))); /* When the pound flag is set the decimal point is included in output regardless of precision. Whether or not a decimal point is included @@ -1557,14 +1559,13 @@ format_floating (const directive &dir) + minprec + 3 /* p+0 */); - res.range.max = format_floating_max (type, 'a', dir.prec[1]); + res.range.max = format_floating_max (type, 'a', prec[1]); res.range.likely = res.range.min; /* The unlikely maximum accounts for the longest multibyte decimal point character. */ res.range.unlikely = res.range.max; - if (dir.prec[0] != dir.prec[1] - || dir.prec[0] == -1 || dir.prec[0] > 0) + if (dir.prec[1] > 0) res.range.unlikely += target_mb_len_max () - 1; break; @@ -1573,23 +1574,18 @@ format_floating (const directive &dir) case 'E': case 'e': { + /* Minimum output attributable to precision and, when it's + non-zero, decimal point. */ + HOST_WIDE_INT minprec = prec[0] ? prec[0] + !radix : 0; + /* The minimum output is "[-+]1.234567e+00" regardless of the value of the actual argument. */ - HOST_WIDE_INT minprec = 6 + !radix /* decimal point */; - if ((dir.prec[0] < 0 && dir.prec[1] > -1) || dir.prec[0] == 0) - minprec = 0; - else if (dir.prec[0] > 0) - minprec = dir.prec[0] + !radix /* decimal point */; - res.range.min = (flagmin + radix + minprec + 2 /* e+ */ + 2); - /* MPFR uses a precision of 16 by default for some reason. - Set it to the C default of 6. */ - int maxprec = dir.prec[1] < 0 ? 6 : dir.prec[1]; - res.range.max = format_floating_max (type, 'e', maxprec); + res.range.max = format_floating_max (type, 'e', prec[1]); res.range.likely = res.range.min; /* The unlikely maximum accounts for the longest multibyte @@ -1605,21 +1601,19 @@ format_floating (const directive &dir) case 'F': case 'f': { + /* Minimum output attributable to precision and, when it's non-zero, + decimal point. */ + HOST_WIDE_INT minprec = prec[0] ? prec[0] + !radix : 0; + /* The lower bound when precision isn't specified is 8 bytes ("1.23456" since precision is taken to be 6). When precision is zero, the lower bound is 1 byte (e.g., "1"). Otherwise, when precision is greater than zero, then the lower bound is 2 plus precision (plus flags). */ - HOST_WIDE_INT minprec = 0; - if (dir.prec[0] < 0) - minprec = dir.prec[1] < 0 ? 6 + !radix /* decimal point */ : 0; - else if (dir.prec[0]) - minprec = dir.prec[0] + !radix /* decimal point */; - res.range.min = flagmin + radix + minprec; /* Compute the upper bound for -TYPE_MAX. */ - res.range.max = format_floating_max (type, 'f', dir.prec[1]); + res.range.max = format_floating_max (type, 'f', prec[1]); /* The minimum output with unknown precision is a single byte (e.g., "0") but the more likely output is 3 bytes ("0.0"). */ @@ -1659,6 +1653,8 @@ format_floating (const directive &dir) else if (maxprec < 0) maxprec = 5; } + else + maxprec = prec[1]; res.range.max = format_floating_max (type, spec, maxprec); @@ -1702,9 +1698,6 @@ format_floating (const directive &dir) static fmtresult format_floating (const directive &dir, tree arg) { - if (!arg || TREE_CODE (arg) != REAL_CST) - return format_floating (dir); - HOST_WIDE_INT prec[] = { dir.prec[0], dir.prec[1] }; /* For an indeterminate precision the lower bound must be assumed @@ -1767,6 +1760,9 @@ format_floating (const directive &dir, tree arg) } } + if (!arg || TREE_CODE (arg) != REAL_CST) + return format_floating (dir, prec); + /* The minimum and maximum number of bytes produced by the directive. */ fmtresult res; diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-15.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-15.c index c38a656..0b863a8 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-15.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-15.c @@ -113,9 +113,16 @@ void test_unknown_precision_integer (int p, int i, double d) void test_unknown_precision_floating (int p, double d) { + T ( 0, "%.*a", R (-1, 0), d); /* { dg-warning "between 6 and 24 " } */ + T ( 6, "%.*a", R (-1, 0), d); /* { dg-warning "writing a terminating nul" } */ + T ( 7, "%.*a", R (-1, 0), d); T ( 7, "%.*a", p, d); T (21, "%.*a", p, 3.141); + T ( 0, "%.*e", R (-1, 0), d); /* { dg-warning "between 5 and 14 " } */ + T ( 0, "%.*e", R (-1, 6), d); /* { dg-warning "between 5 and 14 " } */ + T ( 5, "%.*e", R (-1, 6), d); /* { dg-warning "writing a terminating nul" } */ + T ( 6, "%.*e", R (-1, 6), d); /* "%.0e", 0.0 results in 5 bytes: "0e+00" */ T ( 5, "%.*e", p, d); /* { dg-warning "writing a terminating nul" } */ /* "%#.0e", 0.0 results in 6 bytes: "0.e+00" */ @@ -125,6 +132,10 @@ void test_unknown_precision_floating (int p, double d) T ( 6, "%#.*e", p, 3.141); /* { dg-warning "writing a terminating nul" } */ T ( 7, "%#.*e", p, 3.141); + T ( 0, "%.*f", R (-1, 0), d); /* { dg-warning "between 1 and 317 " } */ + T ( 0, "%.*f", R (-1, 6), d); /* { dg-warning "between 1 and 317 " } */ + T ( 3, "%.*f", R (-1, 6), d); /* { dg-warning "may write a terminating nul" } */ + T ( 4, "%.*f", R (-1, 6), d); /* "%.0f", 0.0 results in 1 byte: "0" but precision of at least 1 is likely, resulting in "0.0". */ T ( 3, "%.*f", p, d); /* { dg-warning "may write a terminating nul" } */ @@ -138,12 +149,16 @@ void test_unknown_precision_floating (int p, double d) T ( 3, "%#.*f", p, 3.141); /* { dg-warning "may write a terminating nul" } */ T ( 4, "%#.*f", p, 3.141); + T ( 0, "%.*g", R (-1, 0), d); /* { dg-warning "between 1 and 13 " } */ + T (12, "%.*g", R (-1, 0), d); /* { dg-warning "may write a terminating nul" } */ + T (13, "%.*g", R (-1, 0), d); T (12, "%.*g", p, d); /* { dg-warning "may write a terminating nul" } */ T (12, "%#.*g", p, d); /* { dg-warning "may write a terminating nul" } */ T (13, "%.*g", p, d); T (13, "%#.*g", p, d); - T ( 6, "%#.*g", R (-1, 0), d);/* { dg-warning "may write a terminating nul" } */ - T ( 7, "%#.*g", R (-1, 0), d); + T (12, "%#.*g", R (-1, 0), d);/* { dg-warning "may write a terminating nul" } */ + T (12, "%#.*g", R (-1, 6), d);/* { dg-warning "may write a terminating nul" } */ + T (13, "%#.*g", R (-1, 0), d); T ( 6, "%#.*g", R ( 0, 0), d);/* { dg-warning "may write a terminating nul" } */ T ( 7, "%#.*g", R ( 0, 0), d); T ( 6, "%#.*g", R ( 0, 1), d);/* { dg-warning "may write a terminating nul" } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr79800.c b/gcc/testsuite/gcc.dg/tree-ssa/pr79800.c new file mode 100644 index 0000000..180a6e7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr79800.c @@ -0,0 +1,31 @@ +/* PR 79800 - wrong snprintf result range with precision in a narrow + negative-positive range + { dg-do "run" } + { dg-options "-O2 -Wall" } */ + +#define FMT "%.*a" +char fmt[] = FMT; + +volatile double x = 1.23456789; + +void f (int p) +{ + if (p < -1 || 0 < p) + p = -1; + + char d[30]; + int n1 = __builtin_sprintf (d, "%.*a", p, x); + const char *s = n1 < 20 ? "< 20" : ">= 20"; + + if (__builtin_strcmp (s, ">= 20")) + __builtin_abort (); +} + +volatile int i = -1; + +int main () +{ + f (i); + + return 0; +}