| From c51f1e7427e6a5ae2a6d82b5a790df77a3adc99a Mon Sep 17 00:00:00 2001 |
| From: Liu Hao <lh_mouse@126.com> |
| Date: Thu, 12 Nov 2020 22:20:29 +0800 |
| Subject: [PATCH] gcc: Add `ll` and `L` length modifiers for `ms_printf` |
| |
| Previous code abused `FMT_LEN_L` for the `I` modifier. As `L` is a |
| valid modifier for `f`, `e`, `g`, etc. and `I` has the same semantics |
| as the C99 `z` modifier, `FMT_LEN_z` is now used instead. |
| |
| First, in the Microsoft ABI, type `long double` has the same layout as |
| type `double`, so `%Lg` behaves identically to `%g`. Users should pass |
| in `double`s instead of `long double`s, as GCC uses the 10-byte format. |
| |
| Second, with a CRT that is recent enough (MSVCRT since Vista, MSVCR80, |
| UCRT, or mingw-w64 8.0), `printf`-family functions can handle the `ll` |
| length modifier correctly. This ability is assumed to be available |
| universally. A lot of libraries (such as libgomp) that use the |
| `format(printf, ...)` attribute used to suffer from warnings about |
| unknown format specifiers. |
| |
| Reference: https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2008/tcxf1dw6(v=vs.90) |
| Reference: https://docs.microsoft.com/en-us/cpp/porting/visual-cpp-what-s-new-2003-through-2015#new-crt-features |
| Signed-off-by: Liu Hao <lh_mouse@126.com> |
| |
| gcc/ChangeLog: |
| * config/i386/msformat-c.c: Add more length modifiers. |
| |
| gcc/testsuite/ChangeLog: |
| * gcc.dg/format/ms_c99-printf-3.c: Update tests. |
| --- |
| gcc/config/i386/msformat-c.c | 53 ++++++++++--------- |
| gcc/testsuite/gcc.dg/format/ms_c99-printf-3.c | 22 +++++++- |
| 2 files changed, 49 insertions(+), 26 deletions(-) |
| |
| diff --git a/gcc/config/i386/msformat-c.c b/gcc/config/i386/msformat-c.c |
| index 4ceec633a6ee6..085ac88789a82 100644 |
| --- a/gcc/config/i386/msformat-c.c |
| +++ b/gcc/config/i386/msformat-c.c |
| @@ -32,10 +32,11 @@ along with GCC; see the file COPYING3. If not see |
| static format_length_info ms_printf_length_specs[] = |
| { |
| { "h", FMT_LEN_h, STD_C89, NULL, FMT_LEN_none, STD_C89, 0 }, |
| - { "l", FMT_LEN_l, STD_C89, NULL, FMT_LEN_none, STD_C89, 0 }, |
| + { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C89, 0 }, |
| + { "L", FMT_LEN_L, STD_C89, NULL, FMT_LEN_none, STD_C89, 1 }, |
| { "I32", FMT_LEN_l, STD_EXT, NULL, FMT_LEN_none, STD_C89, 1 }, |
| { "I64", FMT_LEN_ll, STD_EXT, NULL, FMT_LEN_none, STD_C89, 1 }, |
| - { "I", FMT_LEN_L, STD_EXT, NULL, FMT_LEN_none, STD_C89, 1 }, |
| + { "I", FMT_LEN_z, STD_EXT, NULL, FMT_LEN_none, STD_C89, 1 }, |
| { NULL, FMT_LEN_none, STD_C89, NULL, FMT_LEN_none, STD_C89, 0 } |
| }; |
| |
| @@ -90,33 +91,35 @@ static const format_flag_pair ms_strftime_flag_pairs[] = |
| static const format_char_info ms_print_char_table[] = |
| { |
| /* C89 conversion specifiers. */ |
| - { "di", 0, STD_C89, { T89_I, BADLEN, T89_S, T89_L, T9L_LL, T99_SST, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp0 +'", "i", NULL }, |
| - { "oxX", 0, STD_C89, { T89_UI, BADLEN, T89_US, T89_UL, T9L_ULL, T99_ST, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp0#", "i", NULL }, |
| - { "u", 0, STD_C89, { T89_UI, BADLEN, T89_US, T89_UL, T9L_ULL, T99_ST, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp0'", "i", NULL }, |
| - { "fgG", 0, STD_C89, { T89_D, BADLEN, BADLEN, T99_D, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp0 +#'", "", NULL }, |
| - { "eE", 0, STD_C89, { T89_D, BADLEN, BADLEN, T99_D, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp0 +#", "", NULL }, |
| - { "c", 0, STD_C89, { T89_I, BADLEN, T89_S, T94_WI, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "", NULL }, |
| - { "s", 1, STD_C89, { T89_C, BADLEN, T89_S, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "cR", NULL }, |
| - { "p", 1, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "c", NULL }, |
| - { "n", 1, STD_C89, { T89_I, BADLEN, T89_S, T89_L, T9L_LL, BADLEN, BADLEN, BADLEN, T99_IM, BADLEN, BADLEN, BADLEN }, "", "W", NULL }, |
| + { "di", 0, STD_C89, { T89_I, BADLEN, T89_S, T89_L, T9L_LL, BADLEN, T99_SST, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp0 +'", "i", NULL }, |
| + { "oxX", 0, STD_C89, { T89_UI, BADLEN, T89_US, T89_UL, T9L_ULL, BADLEN, T99_ST, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp0#", "i", NULL }, |
| + { "u", 0, STD_C89, { T89_UI, BADLEN, T89_US, T89_UL, T9L_ULL, BADLEN, T99_ST, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp0'", "i", NULL }, |
| + { "fgG", 0, STD_C89, { T89_D, BADLEN, BADLEN, T99_D, BADLEN, T89_D, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp0 +#'", "", NULL }, |
| + { "eE", 0, STD_C89, { T89_D, BADLEN, BADLEN, T99_D, BADLEN, T89_D, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp0 +#", "", NULL }, |
| + { "c", 0, STD_C89, { T89_I, BADLEN, T89_S, T94_WI, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "", NULL }, |
| + { "s", 1, STD_C89, { T89_C, BADLEN, T89_S, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "cR", NULL }, |
| + { "p", 1, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "c", NULL }, |
| + { "n", 1, STD_C89, { T89_I, BADLEN, T89_S, T89_L, T9L_LL, BADLEN, BADLEN, BADLEN, T99_IM, BADLEN, BADLEN, BADLEN }, "", "W", NULL }, |
| + /* C99 conversion specifiers. */ |
| + { "aA", 0, STD_C99, { T99_D, BADLEN, BADLEN, T99_D, BADLEN, T99_D, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp0 +#", "", NULL }, |
| /* X/Open conversion specifiers. */ |
| - { "C", 0, STD_EXT, { TEX_WI, BADLEN, T89_S, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "", NULL }, |
| - { "S", 1, STD_EXT, { TEX_W, BADLEN, T89_S, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "R", NULL }, |
| + { "C", 0, STD_EXT, { TEX_WI, BADLEN, T89_S, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "", NULL }, |
| + { "S", 1, STD_EXT, { TEX_W, BADLEN, T89_S, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "R", NULL }, |
| { NULL, 0, STD_C89, NOLENGTHS, NULL, NULL, NULL } |
| }; |
| |
| static const format_char_info ms_scan_char_table[] = |
| { |
| /* C89 conversion specifiers. */ |
| - { "di", 1, STD_C89, { T89_I, BADLEN, T89_S, T89_L, T9L_LL, T99_SST, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w'", "W", NULL }, |
| - { "u", 1, STD_C89, { T89_UI, BADLEN, T89_US, T89_UL, T9L_ULL, T99_ST, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w'", "W", NULL }, |
| - { "oxX", 1, STD_C89, { T89_UI, BADLEN, T89_US, T89_UL, T9L_ULL, T99_ST, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w", "W", NULL }, |
| - { "efgEG", 1, STD_C89, { T89_F, BADLEN, BADLEN, T89_D, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w'", "W", NULL }, |
| - { "c", 1, STD_C89, { T89_C, BADLEN, T89_S, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w", "cW", NULL }, |
| - { "s", 1, STD_C89, { T89_C, BADLEN, T89_S, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*aw", "cW", NULL }, |
| - { "[", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*aw", "cW[", NULL }, |
| - { "p", 2, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w", "W", NULL }, |
| - { "n", 1, STD_C89, { T89_I, BADLEN, T89_S, T89_L, T9L_LL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "W", NULL }, |
| + { "di", 1, STD_C89, { T89_I, BADLEN, T89_S, T89_L, T9L_LL, BADLEN, T99_SST, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w'", "W", NULL }, |
| + { "u", 1, STD_C89, { T89_UI, BADLEN, T89_US, T89_UL, T9L_ULL, BADLEN, T99_ST, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w'", "W", NULL }, |
| + { "oxX", 1, STD_C89, { T89_UI, BADLEN, T89_US, T89_UL, T9L_ULL, BADLEN, T99_ST, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w", "W", NULL }, |
| + { "efgEG", 1, STD_C89, { T89_F, BADLEN, BADLEN, T89_D, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w'", "W", NULL }, |
| + { "c", 1, STD_C89, { T89_C, BADLEN, T89_S, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w", "cW", NULL }, |
| + { "s", 1, STD_C89, { T89_C, BADLEN, T89_S, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*aw", "cW", NULL }, |
| + { "[", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*aw", "cW[", NULL }, |
| + { "p", 2, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w", "W", NULL }, |
| + { "n", 1, STD_C89, { T89_I, BADLEN, T89_S, T89_L, T9L_LL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "W", NULL }, |
| /* X/Open conversion specifiers. */ |
| { "C", 1, STD_EXT, { TEX_W, BADLEN, T89_S, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w", "W", NULL }, |
| { "S", 1, STD_EXT, { TEX_W, BADLEN, T89_S, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*aw", "W", NULL }, |
| @@ -182,9 +185,9 @@ extern void TARGET_OVERRIDES_FORMAT_INIT (void); |
| void |
| TARGET_OVERRIDES_FORMAT_INIT (void) |
| { |
| - ms_printf_length_specs[2].std = C89_OR_EXT; /* I32 */ |
| - ms_printf_length_specs[3].std = C89_OR_EXT; /* I64 */ |
| - ms_printf_length_specs[4].std = C89_OR_EXT; /* I */ |
| + ms_printf_length_specs[3].std = C89_OR_EXT; /* I32 */ |
| + ms_printf_length_specs[4].std = C89_OR_EXT; /* I64 */ |
| + ms_printf_length_specs[5].std = C89_OR_EXT; /* I */ |
| } |
| |
| #undef C89_OR_EXT |
| diff --git a/gcc/testsuite/gcc.dg/format/ms_c99-printf-3.c b/gcc/testsuite/gcc.dg/format/ms_c99-printf-3.c |
| index d8c51eaa06385..f46f155a0b17e 100644 |
| --- a/gcc/testsuite/gcc.dg/format/ms_c99-printf-3.c |
| +++ b/gcc/testsuite/gcc.dg/format/ms_c99-printf-3.c |
| @@ -9,13 +9,33 @@ |
| #include "format.h" |
| |
| void |
| -foo (int i, char *s, size_t n, va_list v0, va_list v1, va_list v2, va_list v3, |
| +foo (int i, char *s, size_t n, long l, llong ll, double d, |
| + long double ld, va_list v0, va_list v1, va_list v2, va_list v3, |
| va_list v4, va_list v5, va_list v6, va_list v7) |
| { |
| fprintf (stdout, "%d", i); |
| fprintf (stdout, "%ld", i); /* { dg-warning "format" "fprintf" } */ |
| printf ("%d", i); |
| printf ("%ld", i); /* { dg-warning "format" "printf" } */ |
| + /* These are accepted since MSVCR80, MSVCRT from Vista, UCRT, |
| + * and mingw-w64 8.0 with C99/C++11. */ |
| + printf ("%lld", i); /* { dg-warning "format" "printf" } */ |
| + printf ("%lld", l); /* { dg-warning "format" "printf" } */ |
| + printf ("%lld", ll); |
| + printf ("%llu", i); /* { dg-warning "format" "printf" } */ |
| + printf ("%llu", l); /* { dg-warning "format" "printf" } */ |
| + printf ("%llu", ll); |
| + printf ("%llx", i); /* { dg-warning "format" "printf" } */ |
| + printf ("%llx", l); /* { dg-warning "format" "printf" } */ |
| + printf ("%llx", ll); |
| + /* As MSABI uses an 8-byte `long double`, `%Lg` matches GCC's |
| + * `double` instead of `long double` which takes 10 bytes. */ |
| + printf ("%Lg", d); |
| + printf ("%Lg", ld); /* { dg-warning "format" "printf" } */ |
| + printf ("%Le", d); |
| + printf ("%Le", ld); /* { dg-warning "format" "printf" } */ |
| + printf ("%Lf", d); |
| + printf ("%Lf", ld); /* { dg-warning "format" "printf" } */ |
| /* The "unlocked" functions shouldn't warn in c99 mode. */ |
| fprintf_unlocked (stdout, "%ld", i); |
| printf_unlocked ("%ld", i); |