diff options
author | Jakub Jelinek <jakub@redhat.com> | 2024-12-06 09:09:12 +0100 |
---|---|---|
committer | Jakub Jelinek <jakub@gcc.gnu.org> | 2024-12-06 09:09:12 +0100 |
commit | 0223119f1a6351543c6e96a9735e05cbd4583889 (patch) | |
tree | 4ea879c6ddf61867533ca10755b9474a372aa464 /gcc/testsuite/c-c++-common/cpp | |
parent | 5289540ed58e42ae66255e31f22afe4ca0a6e15e (diff) | |
download | gcc-0223119f1a6351543c6e96a9735e05cbd4583889.zip gcc-0223119f1a6351543c6e96a9735e05cbd4583889.tar.gz gcc-0223119f1a6351543c6e96a9735e05cbd4583889.tar.bz2 |
libcpp, c++: Optimize initializers using #embed in C++
This patch adds similar optimizations to the C++ FE as have been
implemented earlier in the C FE.
The libcpp hunk enables use of CPP_EMBED token even for C++, not just
C; the preprocessor guarantees there is always a CPP_NUMBER CPP_COMMA
before CPP_EMBED and CPP_COMMA CPP_NUMBER after it which simplifies
parsing (unless #embed is more than 2GB, in that case it could be
CPP_NUMBER CPP_COMMA CPP_EMBED CPP_COMMA CPP_EMBED CPP_COMMA CPP_EMBED
CPP_COMMA CPP_NUMBER etc. with each CPP_EMBED covering at most INT_MAX
bytes).
Similarly to the C patch, this patch parses it into RAW_DATA_CST tree
in the braced initializers (and from there peels into INTEGER_CSTs unless
it is an initializer of an std::byte array or integral array with CHAR_BIT
element precision), parses CPP_EMBED in cp_parser_expression into just
the last INTEGER_CST in it because I think users don't need millions of
-Wunused-value warnings because they did useless
int a = (
#embed "megabyte.dat"
);
and so most of the inner INTEGER_CSTs would be there just for the warning,
and in the rest of contexts like template argument list, function argument
list, attribute argument list, ...) parse it into a sequence of INTEGER_CSTs
(I wrote a range/iterator classes to simplify that).
My dumb
cat embed-11.c
constexpr unsigned char a[] = {
#embed "cc1plus"
};
const unsigned char *b = a;
testcase where cc1plus is 492329008 bytes long when configured
--enable-checking=yes,rtl,extra against recent binutils with .base64 gas
support results in:
time ./xg++ -B ./ -S -O2 embed-11.c
real 0m4.350s
user 0m2.427s
sys 0m0.830s
time ./xg++ -B ./ -c -O2 embed-11.c
real 0m6.932s
user 0m6.034s
sys 0m0.888s
(compared to running out of memory or very long compilation).
On a shorter inclusion,
cat embed-12.c
constexpr unsigned char a[] = {
#embed "xg++"
};
const unsigned char *b = a;
where xg++ is 15225904 bytes long, this takes using GCC with the #embed
patchset except for this patch:
time ~/src/gcc/obj36/gcc/xg++ -B ~/src/gcc/obj36/gcc/ -S -O2 embed-12.c
real 0m33.190s
user 0m32.327s
sys 0m0.790s
and with this patch:
time ./xg++ -B ./ -S -O2 embed-12.c
real 0m0.118s
user 0m0.090s
sys 0m0.028s
The patch doesn't change anything on what the first patch in the series
introduces even for C++, namely that #embed is expanded (actually or as if)
into a sequence of literals like
127,69,76,70,2,1,1,3,0,0,0,0,0,0,0,0,2,0,62,0,1,0,0,0,80,211,64,0,0,0,0,0,64,0,0,0,0,0,0,0,8,253
and so each element has int type.
That is how I believe it is in C23, and the different versions of the
C++ P1967 paper specified there some casts, P1967R12 in particular
"Otherwise, the integral constant expression is the value of std::fgetc’s return is cast
to unsigned char."
but please see
https://github.com/llvm/llvm-project/pull/97274#issuecomment-2230929277
comment and whether we really want the preprocessor to preprocess it for
C++ as (or as-if)
static_cast<unsigned char>(127),static_cast<unsigned char>(69),static_cast<unsigned char>(76),static_cast<unsigned char>(70),static_cast<unsigned char>(2),...
i.e. 9 tokens per byte rather than 2, or
(unsigned char)127,(unsigned char)69,...
or
((unsigned char)127),((unsigned char)69),...
etc.
Without a literal suffix for unsigned char constant literals it is horrible,
plus the incompatibility between C and C++. Sure, we could use the magic
form more often for C++ to save the size and do the 9 or how many tokens
form only for the boundary constants and use #embed "." __gnu__::__base64__("...")
for what is in between if there are at least 2 tokens inside of it.
E.g. (unsigned char)127 vs. static_cast<unsigned char>(127) behaves
differently if there is constexpr long long p[] = { ... };
...
#embed __FILE__
[p]
2024-12-06 Jakub Jelinek <jakub@redhat.com>
libcpp/
* files.cc (finish_embed): Use CPP_EMBED even for C++.
gcc/
* tree.h (RAW_DATA_UCHAR_ELT, RAW_DATA_SCHAR_ELT): Define.
gcc/cp/ChangeLog:
* cp-tree.h (class raw_data_iterator): New type.
(class raw_data_range): New type.
* parser.cc (cp_parser_postfix_open_square_expression): Handle
parsing of CPP_EMBED.
(cp_parser_parenthesized_expression_list): Likewise. Use
cp_lexer_next_token_is.
(cp_parser_expression): Handle parsing of CPP_EMBED.
(cp_parser_template_argument_list): Likewise.
(cp_parser_initializer_list): Likewise.
(cp_parser_oacc_clause_tile): Likewise.
(cp_parser_omp_tile_sizes): Likewise.
* pt.cc (tsubst_expr): Handle RAW_DATA_CST.
* constexpr.cc (reduced_constant_expression_p): Likewise.
(raw_data_cst_elt): New function.
(find_array_ctor_elt): Handle RAW_DATA_CST.
(cxx_eval_array_reference): Likewise.
* typeck2.cc (digest_init_r): Emit -Wnarrowing and/or -Wconversion
diagnostics.
(process_init_constructor_array): Handle RAW_DATA_CST.
* decl.cc (maybe_deduce_size_from_array_init): Likewise.
(is_direct_enum_init): Fail for RAW_DATA_CST.
(cp_maybe_split_raw_data): New function.
(consume_init): New function.
(reshape_init_array_1): Add VECTOR_P argument. Handle RAW_DATA_CST.
(reshape_init_array): Adjust reshape_init_array_1 caller.
(reshape_init_vector): Likewise.
(reshape_init_class): Handle RAW_DATA_CST.
(reshape_init_r): Likewise.
gcc/testsuite/
* c-c++-common/cpp/embed-22.c: New test.
* c-c++-common/cpp/embed-23.c: New test.
* g++.dg/cpp/embed-4.C: New test.
* g++.dg/cpp/embed-5.C: New test.
* g++.dg/cpp/embed-6.C: New test.
* g++.dg/cpp/embed-7.C: New test.
* g++.dg/cpp/embed-8.C: New test.
* g++.dg/cpp/embed-9.C: New test.
* g++.dg/cpp/embed-10.C: New test.
* g++.dg/cpp/embed-11.C: New test.
* g++.dg/cpp/embed-12.C: New test.
* g++.dg/cpp/embed-13.C: New test.
* g++.dg/cpp/embed-14.C: New test.
Diffstat (limited to 'gcc/testsuite/c-c++-common/cpp')
-rw-r--r-- | gcc/testsuite/c-c++-common/cpp/embed-22.c | 28 | ||||
-rw-r--r-- | gcc/testsuite/c-c++-common/cpp/embed-23.c | 36 |
2 files changed, 64 insertions, 0 deletions
diff --git a/gcc/testsuite/c-c++-common/cpp/embed-22.c b/gcc/testsuite/c-c++-common/cpp/embed-22.c new file mode 100644 index 0000000..1b35cf9 --- /dev/null +++ b/gcc/testsuite/c-c++-common/cpp/embed-22.c @@ -0,0 +1,28 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -Wno-psabi" } */ +/* { dg-additional-options "-std=c23" { target c } } */ + +typedef unsigned char V __attribute__((vector_size (128))); + +V a; + +void +foo (void) +{ + V b = { + #embed __FILE__ limit (128) gnu::offset (3) + }; + a = b; +} + +const unsigned char c[] = { + #embed __FILE__ limit (128) gnu::offset (3) +}; + +int +main () +{ + foo (); + if (__builtin_memcmp (&c[0], &a, sizeof (a))) + __builtin_abort (); +} diff --git a/gcc/testsuite/c-c++-common/cpp/embed-23.c b/gcc/testsuite/c-c++-common/cpp/embed-23.c new file mode 100644 index 0000000..ea00c6c --- /dev/null +++ b/gcc/testsuite/c-c++-common/cpp/embed-23.c @@ -0,0 +1,36 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ +/* { dg-additional-options "-std=gnu23" { target c } } */ + +typedef unsigned char V __attribute__((vector_size (16))); + +struct S { _Complex double a; V b; int c; }; +struct T { int a; struct S b; int c; struct S d; int e; unsigned char f[22]; _Complex long double g; }; + +const unsigned char a[] = { + #embed __FILE__ limit (124) +}; +const struct T b[2] = { + #embed __FILE__ limit (124) +}; + +int +main () +{ + for (int i = 0; i < 2; ++i) + if (b[i].a != a[i * 62] + || __real__ b[i].b.a != a[i * 62 + 1] + || __imag__ b[i].b.a + || __builtin_memcmp (&b[i].b.b, &a[i * 62 + 2], 16) + || b[i].b.c != a[i * 62 + 18] + || b[i].c != a[i * 62 + 19] + || __real__ b[i].d.a != a[i * 62 + 20] + || __imag__ b[i].d.a + || __builtin_memcmp (&b[i].d.b, &a[i * 62 + 21], 16) + || b[i].d.c != a[i * 62 + 37] + || b[i].e != a[i * 62 + 38] + || __builtin_memcmp (&b[i].f[0], &a[i * 62 + 39], 22) + || __real__ b[i].g != a[i * 62 + 61] + || __imag__ b[i].g) + __builtin_abort (); +} |