On 7/17/24 3:47 AM, Jakub Jelinek wrote:
Hi!
This patch on top of the
https://gcc.gnu.org/pipermail/gcc-patches/2024-June/655012.html
https://gcc.gnu.org/pipermail/gcc-patches/2024-June/655013.html
https://gcc.gnu.org/pipermail/gcc-patches/2024-July/657049.html
patches which just introduce non-optimized support for the C23 feature
and two extensions to it actually optimizes it and on top of the
https://gcc.gnu.org/pipermail/gcc-patches/2024-July/657053.html
patch which adds optimizations to C & middle-end adds similar
optimizations to the C++ FE.
The first 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.
The discussion at that link suggests that the author is planning to
propose removing the cast.
@@ -6895,16 +6918,68 @@ reshape_init_array_1 (tree elt_type, tre
{
tree elt_init;
constructor_elt *old_cur = d->cur;
+ const char *old_ptr = NULL;
+
+ if (TREE_CODE (d->cur->value) == RAW_DATA_CST)
+ old_ptr = RAW_DATA_POINTER (d->cur->value);
Let's call this variable old_raw_data_ptr for clarity, here and in
reshape_init_class.
if (d->cur->index)
CONSTRUCTOR_IS_DESIGNATED_INIT (new_init) = true;
check_array_designated_initializer (d->cur, index);
- elt_init = reshape_init_r (elt_type, d,
- /*first_initializer_p=*/NULL_TREE,
- complain);
+ if (TREE_CODE (d->cur->value) == RAW_DATA_CST
+ && (TREE_CODE (elt_type) == INTEGER_TYPE
+ || (TREE_CODE (elt_type) == ENUMERAL_TYPE
+ && TYPE_CONTEXT (TYPE_MAIN_VARIANT (elt_type)) == std_node
+ && strcmp (TYPE_NAME_STRING (TYPE_MAIN_VARIANT (elt_type)),
+ "byte") == 0))
Maybe is_byte_access_type? Or finally factor out a function to test
specifically for std::byte, it's odd that we don't have one yet.
@@ -7158,6 +7244,7 @@ reshape_init_class (tree type, reshape_i
is initialized by the designated-initializer-list { D }, where D
is the designated- initializer-clause naming a member of the
anonymous union member." */
+ gcc_checking_assert (TREE_CODE (d->cur->value) != RAW_DATA_CST);
Is there a test of trying to use #embed as a designated initializer? I
don't see one.
@@ -7358,9 +7461,16 @@ reshape_init_r (tree type, reshape_iter
valid aggregate initialization. */
&& !first_initializer_p
&& (same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (init))
- || can_convert_arg (type, TREE_TYPE (init), init, LOOKUP_NORMAL,
- complain)))
+ || can_convert_arg (type, TREE_TYPE (init),
+ TREE_CODE (init) == RAW_DATA_CST
+ ? build_int_cst (integer_type_node,
+ *(const unsigned char *)
+ RAW_DATA_POINTER (init))
+ : init,
+ LOOKUP_NORMAL, complain)))
{
+ if (tree raw_init = cp_maybe_split_raw_data (d))
+ return raw_init;
d->cur++;
return init;
This split-or-++ pattern seems to repeat a lot in reshape_init_r, could
we factor it out to avoid problems with people forgetting one or the
other? Maybe consume_init (d) or d->consume_init ()?
Jason