From patchwork Fri Dec 9 18:07:52 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Sebor X-Patchwork-Id: 87541 Delivered-To: patch@linaro.org Received: by 10.140.20.101 with SMTP id 92csp442179qgi; Fri, 9 Dec 2016 10:08:23 -0800 (PST) X-Received: by 10.99.114.89 with SMTP id c25mr145060517pgn.4.1481306903737; Fri, 09 Dec 2016 10:08:23 -0800 (PST) Return-Path: Received: from sourceware.org (server1.sourceware.org. [209.132.180.131]) by mx.google.com with ESMTPS id l26si34850091pli.44.2016.12.09.10.08.23 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 09 Dec 2016 10:08:23 -0800 (PST) Received-SPF: pass (google.com: domain of gcc-patches-return-444042-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-444042-patch=linaro.org@gcc.gnu.org designates 209.132.180.131 as permitted sender) smtp.mailfrom=gcc-patches-return-444042-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:from :subject:to:message-id:date:mime-version:content-type; q=dns; s= default; b=PEK29BFFWKFLSB8/l9YGcMmQGYmFNiaQ9FK939iIbjzrKwFBfA/Uu c2xOqqnPXjfwHNN/g7RtNQ9wfQ4O7ZqXa5STpPG+dUd4Mgv7eWHxzu8GiKAPGVjE 3ssIWfdcmXqYB+EHncfIkbwZoNAzeVNyNeRmzhOz4s1pICw1BSK83s= 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:from :subject:to:message-id:date:mime-version:content-type; s= default; bh=p/Jj5FPI7ww7JFTDjpRMIJp5rA0=; b=HwKsfztCHzZLHHEeu2zK 6vOdRst37kRgxGqh0VZ+hS/IRV2Z/qj6IriSw2n7+i27nZGKNgJoGyCZz9y1y/NC 6CQomY1c3sGx+LUJfS5qBa7qPDX0yxpaD8bAxb3tg58w4QZJE9WL4HNG52ZmSqej /PqIZs4/E8gX7Q2vyO2uBE0= Received: (qmail 106305 invoked by alias); 9 Dec 2016 18:08:02 -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 106291 invoked by uid 89); 9 Dec 2016 18:08:01 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.9 required=5.0 tests=AWL, BAYES_00, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.2 spammy=DBL_MAX, dbl_max, Precision, 3e X-HELO: mail-qk0-f182.google.com Received: from mail-qk0-f182.google.com (HELO mail-qk0-f182.google.com) (209.85.220.182) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 09 Dec 2016 18:07:57 +0000 Received: by mail-qk0-f182.google.com with SMTP id q130so25387869qke.1 for ; Fri, 09 Dec 2016 10:07:57 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:subject:to:message-id:date:user-agent :mime-version; bh=O5bhe7/aE3B07efCrvZOxYuaL5NFea8otFYQWyADMGo=; b=CotofgdumNh1wWa37RW8c/yL8kNHPBsAex0MAcuOdzmh5d4axpNhBghEXkKJ+3kMW2 j5MeoQFDc8cFxnmDXQC66oUfxjbjxgBHxXbLnAV9LsDG3zzIP/kXVDAh+Akf/ZHAlkwY /TmhnP9YQpvJ2R1n1F9d7OUL0ieFOT8je/1dIVYqk/5xzQkBOwkZRVGDrziGTph0kYdW VSVK+EXVFuToVVkmHdH16Fdr7YxQjLXCCO10LuHHUSp4hYn8j3GX4mjWvzL6vwBhEJsk PsGn+F8pibgIHzvA3Ss04oTU1sgGPIjW6kToTtryOaoRUsbgJzEr5ff9VXC67NAnarkg S72g== X-Gm-Message-State: AKaTC00UfEYCptRz6DCNXbeezeZilEXKo1XWbW9PpmpTS5eZXLVl/KVfjezhNvJ5mmQjsQ== X-Received: by 10.55.80.198 with SMTP id e189mr21926955qkb.222.1481306875546; Fri, 09 Dec 2016 10:07:55 -0800 (PST) Received: from [192.168.0.26] (97-124-188-210.hlrn.qwest.net. [97.124.188.210]) by smtp.gmail.com with ESMTPSA id n191sm20364816qke.19.2016.12.09.10.07.54 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 09 Dec 2016 10:07:55 -0800 (PST) From: Martin Sebor Subject: [PATCH] correct %g handling with unknown arguments in -fprintf-return-value (PR 78696) To: Gcc Patch List Message-ID: Date: Fri, 9 Dec 2016 11:07:52 -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 Bug 78696 points out a bug in the handling of the %g directive in the gimple-ssa-sprintf pass where precision wasn't being correctly handled. In the process of developing and testing a fix for it I noticed a few other subtle problems in the floating point handling of unknown values. The attached patch corrects them and adds test cases to better exercise this area. In the same bug, Jakub also pointed out that the -fprintf-retrun-value optimization isn't correct in locales where the floating point radix character (normally the decimal point, '.') is a multibyte character longer than 1 byte. Since that's unrelated to this report I raised bug 78703 and will fix it in a subsequent patch. Martin PR tree-optimization/78696 - [7 Regression] -fprintf-return-value misoptimizes %.Ng where N is greater than 10 gcc/ChangeLog: PR tree-optimization/78696 * gimple-ssa-sprintf.c (format_floating): Correct handling of precision. Use MPFR for %f for greater fidelity. Correct handling of %g. (pass_sprintf_length::compute_format_length): Set width and precision specified by asrerisk to void_node for vararg functions. (try_substitute_return_value): Adjust dump output. gcc/testsuite/ChangeLog: PR tree-optimization/78696 * gcc.dg/tree-ssa/builtin-sprintf-5.c: Add test cases. * gcc.dg/tree-ssa/builtin-sprintf-warn-7.c: New test. * gcc.dg/tree-ssa/builtin-sprintf.c (checkv): Add test cases. diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c index 8de9a1e..3d34342 100644 --- a/gcc/gimple-ssa-sprintf.c +++ b/gcc/gimple-ssa-sprintf.c @@ -474,9 +474,11 @@ struct conversion_spec /* Numeric precision as in "%.32s". */ int precision; - /* Width specified via the '*' character. */ + /* Width specified via the '*' character. Need not be INTEGER_CST. + For vararg functions set to void_node. */ tree star_width; - /* Precision specified via the asterisk. */ + /* Precision specified via the asterisk. Need not be INTEGER_CST. + For vararg functions set to void_node. */ tree star_precision; /* Length modifier. */ @@ -1135,10 +1137,11 @@ format_integer (const conversion_spec &spec, tree arg) } /* Return the number of bytes to format using the format specifier - SPEC the largest value in the real floating TYPE. */ + SPEC and the precision PREC the largest value in the real floating + TYPE. */ static int -format_floating_max (tree type, char spec, int prec = -1) +format_floating_max (tree type, char spec, int prec) { machine_mode mode = TYPE_MODE (type); @@ -1189,7 +1192,6 @@ static fmtresult format_floating (const conversion_spec &spec, int width, int prec) { tree type; - bool ldbl = false; switch (spec.modifier) { @@ -1200,12 +1202,10 @@ format_floating (const conversion_spec &spec, int width, int prec) case FMT_LEN_L: type = long_double_type_node; - ldbl = true; break; case FMT_LEN_ll: type = long_double_type_node; - ldbl = true; break; default: @@ -1215,19 +1215,20 @@ format_floating (const conversion_spec &spec, int width, int prec) /* The minimum and maximum number of bytes produced by the directive. */ fmtresult res; - /* Log10 of of the maximum number of exponent digits for the type. */ - int logexpdigs = 2; + /* The result is always bounded (though the range may be all of int). */ + res.bounded = true; - if (REAL_MODE_FORMAT (TYPE_MODE (type))->b == 2) - { - /* The base in which the exponent is represented should always - be 2 in GCC. */ - - const double log10_2 = .30102999566398119521; + /* The minimum output as determined by flags. It's always at least 1. */ + int flagmin = (1 /* for the first digit */ + + (spec.get_flag ('+') | spec.get_flag (' ')) + + (prec == 0 && spec.get_flag ('#'))); - /* Compute T_MAX_EXP for base 2. */ - int expdigs = REAL_MODE_FORMAT (TYPE_MODE (type))->emax * log10_2; - logexpdigs = ilog (expdigs, 10); + if (width == INT_MIN || prec == INT_MIN) + { + /* When either width or precision is specified but unknown + the upper bound is the maximum. Otherwise it will be + computed for each directive below. */ + res.range.max = target_int_max (); } switch (spec.specifier) @@ -1235,75 +1236,73 @@ format_floating (const conversion_spec &spec, int width, int prec) case 'A': case 'a': { - /* The minimum output is "0x.p+0". */ - res.range.min = 6 + (prec > 0 ? prec : 0); - res.range.max = (width == INT_MIN - ? HOST_WIDE_INT_MAX - : format_floating_max (type, 'a', prec)); - - /* The output of "%a" is fully specified only when precision - is explicitly specified and width isn't unknown. */ - res.bounded = INT_MIN != width && -1 < prec; + /* The minimum output is "0xp+0". */ + res.range.min = flagmin + 5 + (prec > 0 ? prec + 1 : 0); + if (res.range.max == HOST_WIDE_INT_MAX) + { + /* Compute the upper bound for -TYPE_MAX. */ + res.range.max = format_floating_max (type, 'a', prec); + } + break; } case 'E': case 'e': { - bool sign = spec.get_flag ('+') || spec.get_flag (' '); /* The minimum output is "[-+]1.234567e+00" regardless of the value of the actual argument. */ - res.range.min = (sign - + 1 /* unit */ + (prec < 0 ? 7 : prec ? prec + 1 : 0) + res.range.min = (flagmin + + (prec == INT_MIN + ? 0 : prec < 0 ? 7 : prec ? prec + 1 : 0) + 2 /* e+ */ + 2); - /* Unless width is uknown the maximum output is the minimum plus - sign (unless already included), plus the difference between - the minimum exponent of 2 and the maximum exponent for the type. */ - res.range.max = (width == INT_MIN - ? HOST_WIDE_INT_M1U - : res.range.min + !sign + logexpdigs - 2); - - /* "%e" is fully specified and the range of bytes is bounded - unless width is unknown. */ - res.bounded = INT_MIN != width; + + if (res.range.max == HOST_WIDE_INT_MAX) + { + /* MPFR uses a precision of 16 by default for some reason. + Set it to the C default of 6. */ + res.range.max = format_floating_max (type, 'e', + -1 == prec ? 6 : prec); + } break; } case 'F': case 'f': { - /* The minimum output is "1.234567" regardless of the value - of the actual argument. */ - res.range.min = 2 + (prec < 0 ? 6 : prec); - - /* Compute the maximum just once. */ - const int f_max[] = { - format_floating_max (double_type_node, 'f', prec), - format_floating_max (long_double_type_node, 'f', prec) - }; - res.range.max = width == INT_MIN ? HOST_WIDE_INT_MAX : f_max [ldbl]; - - /* "%f" is fully specified and the range of bytes is bounded - unless width is unknown. */ - res.bounded = INT_MIN != width; + /* 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). */ + res.range.min = (flagmin + + (prec != INT_MIN) /* for decimal point */ + + (prec == INT_MIN + ? 0 : prec < 0 ? 6 : prec ? prec : -1)); + + if (res.range.max == HOST_WIDE_INT_MAX) + { + /* Compute the upper bound for -TYPE_MAX. */ + res.range.max = format_floating_max (type, 'f', prec); + } break; } + case 'G': case 'g': { - /* The minimum is the same as for '%F'. */ - res.range.min = 2 + (prec < 0 ? 6 : prec); - - /* Compute the maximum just once. */ - const int g_max[] = { - format_floating_max (double_type_node, 'g', prec), - format_floating_max (long_double_type_node, 'g', prec) - }; - res.range.max = width == INT_MIN ? HOST_WIDE_INT_MAX : g_max [ldbl]; + /* The %g output depends on precision and the exponent of + the argument. Since the value of the argument isn't known + the lower bound on the range of bytes (not counting flags + or width) is 1. */ + res.range.min = flagmin; - /* "%g" is fully specified and the range of bytes is bounded - unless width is unknown. */ - res.bounded = INT_MIN != width; + if (res.range.max == HOST_WIDE_INT_MAX) + { + /* Compute the upper bound for -TYPE_MAX which should be + the lesser of %e and %f. */ + res.range.max = format_floating_max (type, 'g', prec); + } break; } @@ -1313,6 +1312,7 @@ format_floating (const conversion_spec &spec, int width, int prec) if (width > 0) { + /* If width has been specified use it to adjust the range. */ if (res.range.min < (unsigned)width) res.range.min = width; if (res.range.max < (unsigned)width) @@ -1360,11 +1360,7 @@ format_floating (const conversion_spec &spec, tree arg) if (TREE_CODE (spec.star_precision) == INTEGER_CST) prec = tree_to_shwi (spec.star_precision); else - { - /* FIXME: Handle non-constant precision. */ - res.range.min = res.range.max = HOST_WIDE_INT_M1U; - return res; - } + prec = INT_MIN; } else if (res.constant && TOUPPER (spec.specifier) != 'A') { @@ -1375,11 +1371,6 @@ format_floating (const conversion_spec &spec, tree arg) if (res.constant) { - /* Set up an array to easily iterate over. */ - unsigned HOST_WIDE_INT* const minmax[] = { - &res.range.min, &res.range.max - }; - /* Get the real type format desription for the target. */ const REAL_VALUE_TYPE *rvp = TREE_REAL_CST_PTR (arg); const real_format *rfmt = REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (arg))); @@ -1418,16 +1409,32 @@ format_floating (const conversion_spec &spec, tree arg) *pfmt++ = spec.specifier; *pfmt = '\0'; - for (int i = 0; i != sizeof minmax / sizeof *minmax; ++i) + { + /* Set up an array to easily iterate over below. */ + unsigned HOST_WIDE_INT* const minmax[] = { + &res.range.min, &res.range.max + }; + + for (int i = 0; i != sizeof minmax / sizeof *minmax; ++i) + { + /* Use the MPFR rounding specifier to round down in the first + iteration and then up. In most but not all cases this will + result in the same number of bytes. */ + *rndspec = "DU"[i]; + + /* Format it and store the result in the corresponding + member of the result struct. */ + *minmax[i] = mpfr_snprintf (NULL, 0, fmtstr, mpfrval); + } + } + + /* Make sure the minimum is less than the maximum (MPFR rounding + in the call to mpfr_snprintf can result in the reverse. */ + if (res.range.max < res.range.min) { - /* Use the MPFR rounding specifier to round down in the first - iteration and then up. In most but not all cases this will - result in the same number of bytes. */ - *rndspec = "DU"[i]; - - /* Format it and store the result in the corresponding - member of the result struct. */ - *minmax[i] = mpfr_snprintf (NULL, 0, fmtstr, mpfrval); + unsigned HOST_WIDE_INT tmp = res.range.min; + res.range.min = res.range.max; + res.range.max = tmp; } /* The range of output is known even if the result isn't bounded. */ @@ -2263,10 +2270,10 @@ pass_sprintf_length::compute_format_length (const call_info &info, { /* Similarly to the block above, this could be either a POSIX positional argument or a width, depending on what follows. */ - if (gimple_call_num_args (info.callstmt) <= argno) - return false; - - spec.star_width = gimple_call_arg (info.callstmt, argno++); + if (argno < gimple_call_num_args (info.callstmt)) + spec.star_width = gimple_call_arg (info.callstmt, argno++); + else + spec.star_width = void_node; ++pf; } @@ -2342,7 +2349,10 @@ pass_sprintf_length::compute_format_length (const call_info &info, } else if ('*' == *pf) { - spec.star_width = gimple_call_arg (info.callstmt, argno++); + if (argno < gimple_call_num_args (info.callstmt)) + spec.star_width = gimple_call_arg (info.callstmt, argno++); + else + spec.star_width = void_node; ++pf; } else if ('\'' == *pf) @@ -2370,7 +2380,10 @@ pass_sprintf_length::compute_format_length (const call_info &info, } else if ('*' == *pf) { - spec.star_precision = gimple_call_arg (info.callstmt, argno++); + if (argno < gimple_call_num_args (info.callstmt)) + spec.star_precision = gimple_call_arg (info.callstmt, argno++); + else + spec.star_precision = void_node; ++pf; } else @@ -2629,11 +2642,11 @@ try_substitute_return_value (gimple_stmt_iterator *gsi, fprintf (dump_file, " %s-bounds return value in range [%lu, %lu]%s.\n", inbounds, - (unsigned long)res.number_chars_min, - (unsigned long)res.number_chars_max, ign); + (unsigned long)res.number_chars_min - 1, + (unsigned long)res.number_chars_max - 1, ign); else fprintf (dump_file, " %s-bounds return value %lu%s.\n", - inbounds, (unsigned long)res.number_chars, ign); + inbounds, (unsigned long)res.number_chars - 1, ign); } } } diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-5.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-5.c index dbb0dd9..bec6e09 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-5.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-5.c @@ -19,6 +19,7 @@ FAIL (__LINE__)(value); \ } while (0) +/* Verify that EXPECT == snprintf(0, 0, ...). */ #define T(expect, ...) \ do { \ int n = __builtin_snprintf (0, 0, __VA_ARGS__); \ @@ -108,6 +109,7 @@ void test_arg_multiarg (int i, double d) T (16, "%*i %s", 12, i, "abc"); } +/* Verify that EXPECT == vsnprintf(0, 0, ...). */ #define TV(expect, fmt, va) \ do { \ int n = __builtin_vsnprintf (0, 0, fmt, va); \ @@ -117,9 +119,7 @@ void test_arg_multiarg (int i, double d) 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) diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-7.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-7.c new file mode 100644 index 0000000..c8c6405 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-7.c @@ -0,0 +1,276 @@ +/* 78696 - -fprintf-return-value misoptimizes %.Ng where N is greater than 10 + Test to verify the correctness of ranges of output computed for floating + point directives. + { dg-do compile } + { dg-options "-O2 -Wformat -Wformat-length -ftrack-macro-expansion=0" } */ + +typedef __builtin_va_list va_list; + +char dst[1]; + +extern void sink (int, void*); + +/* Macro to test either width or precision specified by the asterisk + (but not both). */ +#define T1(fmt, a) sink (__builtin_sprintf (dst + 1, fmt, a, x), dst) + +/* Macro to test both width and precision specified by the asterisk. */ +#define T2(fmt, w, p) sink (__builtin_sprintf (dst + 1, fmt, w, p, x), dst) + +/* Macro to test vsprintf with both width and precision specified by + the asterisk. */ +#define T(fmt) sink (__builtin_vsprintf (dst + 1, fmt, va), dst) + +/* Exercise %a. */ +void test_a (int w, int p, double x) +{ + T1 ("%.*a", 0); /* { dg-warning "between 6 and 10 bytes" } */ + T1 ("%.*a", 1); /* { dg-warning "between 8 and 12 bytes" } */ + T1 ("%.*a", 2); /* { dg-warning "between 9 and 13 bytes" } */ + T1 ("%.*a", 99); /* { dg-warning "between 106 and 110 bytes" } */ + T1 ("%.*a", 199); /* { dg-warning "between 206 and 210 bytes" } */ + T1 ("%.*a", 1099); /* { dg-warning "between 1106 and 1110 bytes" } */ + + T1 ("%*.a", 0); /* { dg-warning "between 6 and 10 bytes" } */ + T1 ("%*.a", 1); /* { dg-warning "between 6 and 10 bytes" } */ + T1 ("%*.a", 3); /* { dg-warning "between 6 and 10 bytes" } */ + T1 ("%*.a", 6); /* { dg-warning "between 6 and 10 bytes" } */ + T1 ("%*.a", 7); /* { dg-warning "between 7 and 10 bytes" } */ + + T1 ("%*.a", w); /* { dg-warning "writing 6 or more bytes" } */ + T1 ("%*.0a", w); /* { dg-warning "writing 6 or more bytes" } */ + T1 ("%*.1a", w); /* { dg-warning "writing 8 or more bytes" } */ + T1 ("%*.2a", w); /* { dg-warning "writing 9 or more bytes" } */ + + T1 ("%.*a", p); /* { dg-warning "writing 6 or more bytes" } */ + T1 ("%1.*a", p); /* { dg-warning "writing 6 or more bytes" } */ + T1 ("%2.*a", p); /* { dg-warning "writing 6 or more bytes" } */ + T1 ("%3.*a", p); /* { dg-warning "writing 6 or more bytes" } */ + + T2 ("%*.*a", w, p); /* { dg-warning "writing 6 or more bytes" } */ + T2 ("%*.*a", w, p); /* { dg-warning "writing 6 or more bytes" } */ + T2 ("%*.*a", w, p); /* { dg-warning "writing 6 or more bytes" } */ +} + +/* Exercise %e. */ +void test_e (int w, int p, double x) +{ + T1 ("%.*e", 0); /* { dg-warning "between 5 and 7 bytes" } */ + T1 ("%.*e", 1); /* { dg-warning "between 7 and 9 bytes" } */ + T1 ("%.*e", 2); /* { dg-warning "between 8 and 10 bytes" } */ + T1 ("%.*e", 99); /* { dg-warning "between 105 and 107 bytes" } */ + T1 ("%.*e", 199); /* { dg-warning "between 205 and 207 bytes" } */ + T1 ("%.*e", 1099); /* { dg-warning "between 1105 and 1107 bytes" } */ + + T1 ("%*.e", 0); /* { dg-warning "between 5 and 7 bytes" } */ + T1 ("%*.e", 1); /* { dg-warning "between 5 and 7 bytes" } */ + T1 ("%*.e", 1); /* { dg-warning "between 5 and 7 bytes" } */ + T1 ("%*.e", 3); /* { dg-warning "between 5 and 7 bytes" } */ + T1 ("%*.e", 6); /* { dg-warning "between 6 and 7 bytes" } */ + T1 ("%*.e", 7); /* { dg-warning "writing 7 bytes" } */ + + T1 ("%*.e", w); /* { dg-warning "writing 5 or more bytes" } */ + T1 ("%*.0e", w); /* { dg-warning "writing 5 or more bytes" } */ + T1 ("%*.1e", w); /* { dg-warning "writing 7 or more bytes" } */ + T1 ("%*.2e", w); /* { dg-warning "writing 8 or more bytes" } */ + + T1 ("%.*e", p); /* { dg-warning "writing 5 or more bytes" } */ + T1 ("%1.*e", p); /* { dg-warning "writing 5 or more bytes" } */ + T1 ("%2.*e", p); /* { dg-warning "writing 5 or more bytes" } */ + T1 ("%3.*e", p); /* { dg-warning "writing 5 or more bytes" } */ + + T2 ("%*.*e", w, p); /* { dg-warning "writing 5 or more bytes" } */ + T2 ("%*.*e", w, p); /* { dg-warning "writing 5 or more bytes" } */ + T2 ("%*.*e", w, p); /* { dg-warning "writing 5 or more bytes" } */ +} + +/* Exercise %f. */ +void test_f (int w, int p, double x) +{ + T1 ("%.*f", 0); /* { dg-warning "between 1 and 310 bytes" } */ + T1 ("%.*f", 1); /* { dg-warning "between 3 and 312 bytes" } */ + T1 ("%.*f", 2); /* { dg-warning "between 4 and 313 bytes" } */ + T1 ("%.*f", 99); /* { dg-warning "between 101 and 410 bytes" } */ + T1 ("%.*f", 199); /* { dg-warning "between 201 and 510 bytes" } */ + T1 ("%.*f", 1099); /* { dg-warning "between 1101 and 1410 bytes" } */ + + T2 ("%*.*f", 0, 0); /* { dg-warning "between 1 and 310 bytes" } */ + T2 ("%*.*f", 1, 0); /* { dg-warning "between 1 and 310 bytes" } */ + T2 ("%*.*f", 2, 0); /* { dg-warning "between 2 and 310 bytes" } */ + T2 ("%*.*f", 3, 0); /* { dg-warning "between 3 and 310 bytes" } */ + T2 ("%*.*f", 310, 0); /* { dg-warning "writing 310 bytes" } */ + T2 ("%*.*f", 311, 0); /* { dg-warning "writing 311 bytes" } */ + T2 ("%*.*f", 312, 312); /* { dg-warning "between 314 and 623 bytes" } */ + T2 ("%*.*f", 312, 313); /* { dg-warning "between 315 and 624 bytes" } */ + + T1 ("%*.f", w); /* { dg-warning "writing 1 or more bytes" } */ + T1 ("%*.0f", w); /* { dg-warning "writing 1 or more bytes" } */ + T1 ("%*.1f", w); /* { dg-warning "writing 3 or more bytes" } */ + T1 ("%*.2f", w); /* { dg-warning "writing 4 or more bytes" } */ + + T1 ("%.*f", p); /* { dg-warning "writing 1 or more bytes" } */ + T1 ("%1.*f", p); /* { dg-warning "writing 1 or more bytes" } */ + T1 ("%2.*f", p); /* { dg-warning "writing 2 or more bytes" } */ + T1 ("%3.*f", p); /* { dg-warning "writing 3 or more bytes" } */ + + T2 ("%*.*f", w, p); /* { dg-warning "writing 1 or more bytes" } */ + T2 ("%*.*f", w, p); /* { dg-warning "writing 1 or more bytes" } */ + T2 ("%*.*f", w, p); /* { dg-warning "writing 1 or more bytes" } */ +} + +/* Exercise %g. The expected output is the lesser of %e and %f. */ +void test_g (double x) +{ + T1 ("%.*g", 0); /* { dg-warning "between 1 and 7 bytes" } */ + T1 ("%.*g", 1); /* { dg-warning "between 1 and 7 bytes" } */ + T1 ("%.*g", 2); /* { dg-warning "between 1 and 9 bytes" } */ + T1 ("%.*g", 99); /* { dg-warning "between 1 and 106 bytes" } */ + T1 ("%.*g", 199); /* { dg-warning "between 1 and 206 bytes" } */ + T1 ("%.*g", 1099); /* { dg-warning "between 1 and 310 bytes" } */ + + T2 ("%*.*g", 0, 0); /* { dg-warning "between 1 and 7 bytes" } */ + T2 ("%*.*g", 1, 0); /* { dg-warning "between 1 and 7 bytes" } */ + T2 ("%*.*g", 2, 0); /* { dg-warning "between 2 and 7 bytes" } */ + T2 ("%*.*g", 3, 0); /* { dg-warning "between 3 and 7 bytes" } */ + T2 ("%*.*g", 7, 0); /* { dg-warning "writing 7 bytes" } */ + T2 ("%*.*g", 310, 0); /* { dg-warning "writing 310 bytes" } */ + T2 ("%*.*g", 311, 0); /* { dg-warning "writing 311 bytes" } */ + T2 ("%*.*g", 312, 312); /* { dg-warning "writing 312 bytes" } */ + T2 ("%*.*g", 312, 313); /* { dg-warning "writing 312 bytes" } */ + T2 ("%*.*g", 333, 999); /* { dg-warning "writing 333 bytes" } */ +} + +/* Exercise %a. */ +void test_a_va (va_list va) +{ + T ("%.0a"); /* { dg-warning "between 6 and 10 bytes" } */ + T ("%.1a"); /* { dg-warning "between 8 and 12 bytes" } */ + T ("%.2a"); /* { dg-warning "between 9 and 13 bytes" } */ + T ("%.99a"); /* { dg-warning "between 106 and 110 bytes" } */ + T ("%.199a"); /* { dg-warning "between 206 and 210 bytes" } */ + T ("%.1099a"); /* { dg-warning "between 1106 and 1110 bytes" } */ + + T ("%0.a"); /* { dg-warning "between 6 and 10 bytes" } */ + T ("%1.a"); /* { dg-warning "between 6 and 10 bytes" } */ + T ("%3.a"); /* { dg-warning "between 6 and 10 bytes" } */ + T ("%6.a"); /* { dg-warning "between 6 and 10 bytes" } */ + T ("%7.a"); /* { dg-warning "between 7 and 10 bytes" } */ + + T ("%*.a"); /* { dg-warning "writing 6 or more bytes" } */ + T ("%*.0a"); /* { dg-warning "writing 6 or more bytes" } */ + T ("%*.1a"); /* { dg-warning "writing 8 or more bytes" } */ + T ("%*.2a"); /* { dg-warning "writing 9 or more bytes" } */ + + T ("%.*a"); /* { dg-warning "writing 6 or more bytes" } */ + T ("%1.*a"); /* { dg-warning "writing 6 or more bytes" } */ + T ("%2.*a"); /* { dg-warning "writing 6 or more bytes" } */ + T ("%6.*a"); /* { dg-warning "writing 6 or more bytes" } */ + T ("%9.*a"); /* { dg-warning "writing 9 or more bytes" } */ + + T ("%*.*a"); /* { dg-warning "writing 6 or more bytes" } */ +} + +/* Exercise %e. */ +void test_e_va (va_list va) +{ + T ("%e"); /* { dg-warning "between 12 and 14 bytes" } */ + T ("%+e"); /* { dg-warning "between 13 and 14 bytes" } */ + T ("% e"); /* { dg-warning "between 13 and 14 bytes" } */ + T ("%#e"); /* { dg-warning "between 12 and 14 bytes" } */ + T ("%#+e"); /* { dg-warning "between 13 and 14 bytes" } */ + T ("%# e"); /* { dg-warning "between 13 and 14 bytes" } */ + + T ("%.e"); /* { dg-warning "between 5 and 7 bytes" } */ + T ("%.0e"); /* { dg-warning "between 5 and 7 bytes" } */ + T ("%.1e"); /* { dg-warning "between 7 and 9 bytes" } */ + T ("%.2e"); /* { dg-warning "between 8 and 10 bytes" } */ + T ("%.99e"); /* { dg-warning "between 105 and 107 bytes" } */ + T ("%.199e"); /* { dg-warning "between 205 and 207 bytes" } */ + T ("%.1099e"); /* { dg-warning "between 1105 and 1107 bytes" } */ + + T ("%0.e"); /* { dg-warning "between 5 and 7 bytes" } */ + T ("%1.e"); /* { dg-warning "between 5 and 7 bytes" } */ + T ("%1.e"); /* { dg-warning "between 5 and 7 bytes" } */ + T ("%3.e"); /* { dg-warning "between 5 and 7 bytes" } */ + T ("%6.e"); /* { dg-warning "between 6 and 7 bytes" } */ + T ("%7.e"); /* { dg-warning "writing 7 bytes" } */ + + T ("%.*e"); /* { dg-warning "writing 5 or more bytes" } */ + T ("%1.*e"); /* { dg-warning "writing 5 or more bytes" } */ + T ("%6.*e"); /* { dg-warning "writing 6 or more bytes" } */ + T ("%9.*e"); /* { dg-warning "writing 9 or more bytes" } */ + + T ("%*.*e"); /* { dg-warning "writing 5 or more bytes" } */ +} + +/* Exercise %f. */ +void test_f_va (va_list va) +{ + T ("%f"); /* { dg-warning "between 8 and 317 bytes" } */ + T ("%+f"); /* { dg-warning "between 9 and 317 bytes" } */ + T ("% f"); /* { dg-warning "between 9 and 317 bytes" } */ + T ("%#f"); /* { dg-warning "between 8 and 317 bytes" } */ + T ("%+f"); /* { dg-warning "between 9 and 317 bytes" } */ + T ("% f"); /* { dg-warning "between 9 and 317 bytes" } */ + T ("%#+f"); /* { dg-warning "between 9 and 317 bytes" } */ + T ("%# f"); /* { dg-warning "between 9 and 317 bytes" } */ + + T ("%.f"); /* { dg-warning "between 1 and 310 bytes" } */ + T ("%.0f"); /* { dg-warning "between 1 and 310 bytes" } */ + T ("%.1f"); /* { dg-warning "between 3 and 312 bytes" } */ + T ("%.2f"); /* { dg-warning "between 4 and 313 bytes" } */ + T ("%.99f"); /* { dg-warning "between 101 and 410 bytes" } */ + T ("%.199f"); /* { dg-warning "between 201 and 510 bytes" } */ + T ("%.1099f"); /* { dg-warning "between 1101 and 1410 bytes" } */ + + T ("%0.0f"); /* { dg-warning "between 1 and 310 bytes" } */ + T ("%1.0f"); /* { dg-warning "between 1 and 310 bytes" } */ + T ("%2.0f"); /* { dg-warning "between 2 and 310 bytes" } */ + T ("%3.0f"); /* { dg-warning "between 3 and 310 bytes" } */ + T ("%310.0f"); /* { dg-warning "writing 310 bytes" } */ + T ("%311.0f"); /* { dg-warning "writing 311 bytes" } */ + T ("%312.312f"); /* { dg-warning "between 314 and 623 bytes" } */ + T ("%312.313f"); /* { dg-warning "between 315 and 624 bytes" } */ + + T ("%.*f"); /* { dg-warning "writing 1 or more bytes" } */ + T ("%1.*f"); /* { dg-warning "writing 1 or more bytes" } */ + T ("%3.*f"); /* { dg-warning "writing 3 or more bytes" } */ + + T ("%*.*f"); /* { dg-warning "writing 1 or more bytes" } */ +} + +/* Exercise %g. The expected output is the lesser of %e and %f. */ +void test_g_va (va_list va) +{ + T ("%g"); /* { dg-warning "between 1 and 13 bytes" } */ + T ("%+g"); /* { dg-warning "between 2 and 13 bytes" } */ + T ("% g"); /* { dg-warning "between 2 and 13 bytes" } */ + T ("%#g"); /* { dg-warning "between 1 and 13 bytes" } */ + T ("%#+g"); /* { dg-warning "between 2 and 13 bytes" } */ + T ("%# g"); /* { dg-warning "between 2 and 13 bytes" } */ + + T ("%.g"); /* { dg-warning "between 1 and 7 bytes" } */ + T ("%.0g"); /* { dg-warning "between 1 and 7 bytes" } */ + T ("%.1g"); /* { dg-warning "between 1 and 7 bytes" } */ + T ("%.2g"); /* { dg-warning "between 1 and 9 bytes" } */ + T ("%.99g"); /* { dg-warning "between 1 and 106 bytes" } */ + T ("%.199g"); /* { dg-warning "between 1 and 206 bytes" } */ + T ("%.1099g"); /* { dg-warning "between 1 and 310 bytes" } */ + + T ("%0.0g"); /* { dg-warning "between 1 and 7 bytes" } */ + T ("%1.0g"); /* { dg-warning "between 1 and 7 bytes" } */ + T ("%2.0g"); /* { dg-warning "between 2 and 7 bytes" } */ + T ("%3.0g"); /* { dg-warning "between 3 and 7 bytes" } */ + T ("%7.0g"); /* { dg-warning "writing 7 bytes" } */ + T ("%310.0g"); /* { dg-warning "writing 310 bytes" } */ + T ("%311.0g"); /* { dg-warning "writing 311 bytes" } */ + T ("%312.312g"); /* { dg-warning "writing 312 bytes" } */ + T ("%312.313g"); /* { dg-warning "writing 312 bytes" } */ + T ("%333.999g"); /* { dg-warning "writing 333 bytes" } */ + + T ("%.*g"); /* { dg-warning "writing 1 or more bytes" } */ + T ("%1.*g"); /* { dg-warning "writing 1 or more bytes" } */ + T ("%4.*g"); /* { dg-warning "writing 4 or more bytes" } */ + + T ("%*.*g"); /* { dg-warning "writing 1 or more bytes" } */ +} diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf.c index 916df79..b3d6128 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf.c @@ -56,9 +56,12 @@ checkv (const char *func, int line, int res, int min, int max, fail = 1; } - else + else if (min == max) __builtin_printf ("PASS: %s:%i: \"%s\" result %i: \"%s\"\n", func, line, fmt, n, dst); + else + __builtin_printf ("PASS: %s:%i: \"%s\" result %i in [%i, %i]: \"%s\"\n", + func, line, fmt, n, min, max, dst); } if (fail) @@ -75,7 +78,7 @@ check (const char *func, int line, int res, int min, int max, __builtin_va_end (va); } -char buffer[256]; +char buffer[4100]; char* volatile dst = buffer; char* ptr = buffer; @@ -368,6 +371,8 @@ test_a_double (double d) EQL ( 9, 10, "%.2a", 4.0); /* 0x8.00p-1 */ EQL (10, 11, "%.3a", 5.0); /* 0xa.000p-1 */ + EQL (11, 12, "%.*a", 4, 6.0); /* 0xc.0000p-1 */ + EQL (12, 13, "%.*a", 5, 7.0); /* 0xe.00000p-1 */ /* d is in [ 0, -DBL_MAX ] */ RNG ( 6, 10, 11, "%.0a", d); /* 0x0p+0 ... -0x2p+1023 */ RNG ( 6, 12, 13, "%.1a", d); /* 0x0p+0 ... -0x2.0p+1023 */ @@ -385,7 +390,7 @@ test_a_long_double (void) } static void __attribute__ ((noinline, noclone)) -test_e_double (void) +test_e_double (double d) { EQL (12, 13, "%e", 1.0e0); EQL (13, 14, "%e", -1.0e0); @@ -407,10 +412,34 @@ test_e_double (void) EQL (12, 13, "%e", 1.0e-1); EQL (12, 13, "%e", 1.0e-12); EQL (13, 14, "%e", 1.0e-123); + + RNG (12, 14, 15, "%e", d); + RNG ( 5, 7, 8, "%.e", d); + RNG ( 5, 7, 8, "%.0e", d); + RNG ( 7, 9, 10, "%.1e", d); + RNG ( 8, 10, 11, "%.2e", d); + RNG ( 9, 11, 12, "%.3e", d); + RNG (10, 12, 13, "%.4e", d); + RNG (11, 13, 14, "%.5e", d); + RNG (12, 14, 15, "%.6e", d); + RNG (13, 15, 16, "%.7e", d); + + RNG (4006, 4008, 4009, "%.4000e", d); + + RNG ( 5, 7, 8, "%.*e", 0, d); + RNG ( 7, 9, 10, "%.*e", 1, d); + RNG ( 8, 10, 11, "%.*e", 2, d); + RNG ( 9, 11, 12, "%.*e", 3, d); + RNG (10, 12, 13, "%.*e", 4, d); + RNG (11, 13, 14, "%.*e", 5, d); + RNG (12, 14, 15, "%.*e", 6, d); + RNG (13, 15, 16, "%.*e", 7, d); + + RNG (4006, 4008, 4009, "%.*e", 4000, d); } static void __attribute__ ((noinline, noclone)) -test_e_long_double (void) +test_e_long_double (long double d) { EQL (12, 13, "%Le", 1.0e0L); EQL (13, 14, "%Le", -1.0e0L); @@ -443,10 +472,32 @@ test_e_long_double (void) EQL ( 8, 9, "%.1Le", 1.0e-111L); EQL (19, 20, "%.12Le", 1.0e-112L); EQL (20, 21, "%.13Le", 1.0e-113L); + + /* The following correspond to the double results plus 1 for the upper + bound accounting for the four-digit exponent. */ + RNG (12, 15, 16, "%Le", d); /* 0.000000e+00 ... -1.189732e+4932 */ + RNG ( 5, 8, 9, "%.Le", d); + RNG ( 5, 9, 10, "%.0Le", d); + RNG ( 7, 10, 11, "%.1Le", d); /* 0.0e+00 ... -1.2e+4932 */ + RNG ( 8, 11, 12, "%.2Le", d); /* 0.00e+00 ... -1.19e+4932 */ + RNG ( 9, 12, 13, "%.3Le", d); + RNG (10, 13, 14, "%.4Le", d); + RNG (11, 14, 15, "%.5Le", d); + RNG (12, 15, 16, "%.6Le", d); /* same as plain "%Le" */ + RNG (13, 16, 17, "%.7Le", d); /* 0.0000000e+00 ... -1.1897315e+4932 */ + + RNG ( 5, 9, 10, "%.*Le", 0, d); + RNG ( 7, 10, 11, "%.*Le", 1, d); + RNG ( 8, 11, 12, "%.*Le", 2, d); + RNG ( 9, 12, 13, "%.*Le", 3, d); + RNG (10, 13, 14, "%.*Le", 4, d); + RNG (11, 14, 15, "%.*Le", 5, d); + RNG (12, 15, 16, "%.*Le", 6, d); + RNG (13, 16, 17, "%.*Le", 7, d); } static void __attribute__ ((noinline, noclone)) -test_f_double (void) +test_f_double (double d) { EQL ( 8, 9, "%f", 0.0e0); EQL ( 8, 9, "%f", 0.1e0); @@ -464,6 +515,8 @@ test_f_double (void) EQL ( 8, 9, "%f", 1.0e-1); EQL ( 8, 9, "%f", 1.0e-12); EQL ( 8, 9, "%f", 1.0e-123); + + RNG ( 8, 317, 318, "%f", d); } static void __attribute__ ((noinline, noclone)) @@ -488,6 +541,87 @@ test_f_long_double (void) } static void __attribute__ ((noinline, noclone)) +test_g_double (double d) +{ + /* Numbers exactly representable in binary floating point. */ + EQL ( 1, 2, "%g", 0.0); + EQL ( 3, 4, "%g", 1.0 / 2); + EQL ( 4, 5, "%g", 1.0 / 4); + EQL ( 5, 6, "%g", 1.0 / 8); + EQL ( 6, 7, "%g", 1.0 / 16); + EQL ( 7, 8, "%g", 1.0 / 32); + EQL ( 8, 9, "%g", 1.0 / 64); + EQL ( 9, 10, "%g", 1.0 / 128); + EQL ( 10, 11, "%g", 1.0 / 256); + EQL ( 10, 11, "%g", 1.0 / 512); + + /* Numbers that are not exactly representable. */ + RNG ( 3, 8, 9, "%g", 0.1); + RNG ( 4, 8, 9, "%g", 0.12); + RNG ( 5, 8, 9, "%g", 0.123); + RNG ( 6, 8, 9, "%g", 0.1234); + RNG ( 7, 8, 9, "%g", 0.12345); + RNG ( 8, 8, 9, "%g", 0.123456); + + RNG ( 4, 7, 8, "%g", 0.123e+1); + EQL ( 8, 9, "%g", 0.123e+12); + RNG ( 9, 12, 13, "%g", 0.123e+134); + + RNG ( 1, 13, 14, "%g", d); + RNG ( 1, 7, 8, "%.g", d); + RNG ( 1, 7, 8, "%.0g", d); + RNG ( 1, 7, 8, "%.1g", d); + RNG ( 1, 9, 10, "%.2g", d); + RNG ( 1, 10, 11, "%.3g", d); + RNG ( 1, 11, 12, "%.4g", d); + RNG ( 1, 12, 13, "%.5g", d); + RNG ( 1, 13, 14, "%.6g", d); + RNG ( 1, 14, 15, "%.7g", d); + RNG ( 1, 15, 16, "%.8g", d); + + RNG ( 1,310,311, "%.9999g", d); + + RNG ( 1, 7, 8, "%.*g", 0, d); + RNG ( 1, 7, 8, "%.*g", 1, d); + RNG ( 1, 9, 10, "%.*g", 2, d); + RNG ( 1, 10, 11, "%.*g", 3, d); + RNG ( 1, 11, 12, "%.*g", 4, d); + RNG ( 1, 12, 13, "%.*g", 5, d); + RNG ( 1, 13, 14, "%.*g", 6, d); + RNG ( 1, 14, 15, "%.*g", 7, d); + RNG ( 1, 15, 16, "%.*g", 8, d); + RNG ( 1,310,311, "%.*g", 9999, d); +} + +static void __attribute__ ((noinline, noclone)) +test_g_long_double (void) +{ + /* Numbers exactly representable in binary floating point. */ + EQL ( 1, 2, "%Lg", 0.0L); + EQL ( 3, 4, "%Lg", 1.0L / 2); + EQL ( 4, 5, "%Lg", 1.0L / 4); + EQL ( 5, 6, "%Lg", 1.0L / 8); + EQL ( 6, 7, "%Lg", 1.0L / 16); + EQL ( 7, 8, "%Lg", 1.0L / 32); + EQL ( 8, 9, "%Lg", 1.0L / 64); + EQL ( 9, 10, "%Lg", 1.0L / 128); + EQL ( 10, 11, "%Lg", 1.0L / 256); + EQL ( 10, 11, "%Lg", 1.0L / 512); + + /* Numbers that are not exactly representable. */ + RNG ( 3, 8, 9, "%Lg", 0.1L); + RNG ( 4, 8, 9, "%Lg", 0.12L); + RNG ( 5, 8, 9, "%Lg", 0.123L); + RNG ( 6, 8, 9, "%Lg", 0.1234L); + RNG ( 7, 8, 9, "%Lg", 0.12345L); + RNG ( 8, 8, 9, "%Lg", 0.123456L); + + RNG ( 4, 7, 8, "%Lg", 0.123e+1L); + EQL ( 8, 9, "%Lg", 0.123e+12L); + RNG ( 9, 12, 13, "%Lg", 0.123e+134L); +} + +static void __attribute__ ((noinline, noclone)) test_s (int i) { EQL ( 0, 1, "%s", ""); @@ -530,12 +664,14 @@ int main (void) test_x ('?', 0xdead, 0xdeadbeef); test_a_double (0.0); - test_e_double (); - test_f_double (); + test_e_double (0.0); + test_f_double (0.0); + test_g_double (0.0); test_a_long_double (); - test_e_long_double (); + test_e_long_double (0.0); test_f_long_double (); + test_g_long_double (); test_s (0);