https://gcc.gnu.org/g:0229b5b3da003647f1dfd0941f8b8af762a69f1d
commit r16-8439-g0229b5b3da003647f1dfd0941f8b8af762a69f1d Author: Jakub Jelinek <[email protected]> Date: Fri Apr 3 10:18:25 2026 +0200 c++: Handle RAW_DATA_CST and RANGE_EXPR in build_vec_init [PR124531] The following testcases show two bugs in build_vec_init, one introduced with either since my r15-5958 (when using #embed) or my r15-6339 (when not using it but large init transformed into RAW_DATA_CST), problem that the FOR_EACH_CONSTRUCTOR_ELT loop in build_vec_init doesn't handle RAW_DATA_CST, and another since Marek's r15-7810 which has added limited RANGE_EXPR support to that loop, but only changed the num_initialized_elts value computation and has not actually also added a runtime loop over the range to initialize multiple elements to the same value. The lack of RAW_DATA_CST handling causes ICEs (during expansion or later on), while the lack of proper RANGE_EXPR handling causes wrong-code. The following patch attempts to fix both. RAW_DATA_CST has 2 separate variants of handling it, one is when the types match (digested is true) and it is char/signed char/unsigned char/std::byte array, in that case (especially if it is huge initializer, but RAW_DATA_CST already implies 62+ elements) it emits a setting of MEM_REF with ARRAY_TYPE for the RAW_DATA_LENGTH bytes to a CONSTRUCTOR with the RAW_DATA_CST in it which gimplifier handles (but of course for try_const const_vec it uses the RAW_DATA_CST directly). The second variant is for other types or non-digested one, in that case RAW_DATA_CST is peeled appart into individual INTEGER_CSTs. As for RANGE_EXPR, for try_const const_vec it uses the RANGE_EXPR field as before, but for the runtime code it puts the one_init/base increment/iterator decrement stmts into a loop which iterates range_expr_nelts times. The reason for the first hunk is to optimize the CONSTRUCTOR from what the preprocessor emits, i.e. CPP_NUMBER CPP_COMMA CPP_EMBED CPP_COMMA CPP_NUMBER turned into INTEGER_CST RAW_DATA_CST INTEGER_CST into just RAW_DATA_CST covering also the first and last number. I'm worried about braced_lists_to_strings transformation to STRING_CST when try_const though, I think it isn't handled right now, so the code ignores STRING_CST return. 2026-04-03 Jakub Jelinek <[email protected]> PR c++/124531 * init.cc (build_vec_init): Call braced_lists_to_strings for array CONSTRUCTORs. Handle RAW_DATA_CST and handle RANGE_EXPR correctly. * g++.dg/cpp/embed-29.C: New test. * g++.dg/cpp0x/pr124531.C: New test. Reviewed-by: Jason Merrill <[email protected]> Diff: --- gcc/cp/init.cc | 126 ++++++++++++++++++++++++++++++++-- gcc/testsuite/g++.dg/cpp/embed-29.C | 24 +++++++ gcc/testsuite/g++.dg/cpp0x/pr124531.C | 56 +++++++++++++++ 3 files changed, 200 insertions(+), 6 deletions(-) diff --git a/gcc/cp/init.cc b/gcc/cp/init.cc index 022485c3258d..d0929c35bfcb 100644 --- a/gcc/cp/init.cc +++ b/gcc/cp/init.cc @@ -4862,6 +4862,23 @@ build_vec_init (tree base, tree maxindex, tree init, some are non-constant. */ bool do_static_init = (DECL_P (obase) && TREE_STATIC (obase)); + if (init + && TREE_CODE (init) == CONSTRUCTOR + && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE + && INTEGRAL_TYPE_P (type) + && same_type_p (type, TREE_TYPE (TREE_TYPE (init)))) + { + /* If RAW_DATA_CST is in the CONSTRUCTOR, we want to optimize + INTEGER_CST, RAW_DATA_CST, INTEGER_CST into just + RAW_DATA_CST larger by 2 chars if possible. But at least + for now punt if braced_lists_to_strings instead turns it + into a STRING_CST in the try_const case, as the STRING_CST + case doesn't handle try_const. */ + tree new_init = braced_lists_to_strings (TREE_TYPE (init), init); + if (!try_const || TREE_CODE (new_init) == CONSTRUCTOR) + init = new_init; + } + bool empty_list = false; if (init && BRACE_ENCLOSED_INITIALIZER_P (init) && CONSTRUCTOR_NELTS (init) == 0) @@ -4875,6 +4892,11 @@ build_vec_init (tree base, tree maxindex, tree init, /* Do non-default initialization of non-trivial arrays resulting from brace-enclosed initializers. */ unsigned HOST_WIDE_INT idx; + /* Used in RAW_DATA_CST handling below if we need to expand it + (not digested char-sized integer type). It is -1 when not peeling + off such RAW_DATA_CST, otherwise indicates which index from + the RAW_DATA_CST has been handled most recently. */ + unsigned int raw_idx = -1; tree field, elt; /* If the constructor already has the array type, it's been through digest_init, so we shouldn't try to do anything more. */ @@ -4892,13 +4914,76 @@ build_vec_init (tree base, tree maxindex, tree init, FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), idx, field, elt) { - tree baseref = build1 (INDIRECT_REF, type, base); tree one_init; - if (field && TREE_CODE (field) == RANGE_EXPR) - num_initialized_elts += range_expr_nelts (field); - else - num_initialized_elts++; + if (TREE_CODE (elt) == RAW_DATA_CST) + { + if (digested + && (TREE_CODE (type) == INTEGER_TYPE + || is_byte_access_type (type)) + && TYPE_PRECISION (type) == CHAR_BIT) + { + /* If possible, handle RAW_DATA_CST as ARRAY_TYPE + copy from ctor to MEM_REF. */ + tree atype + = build_array_of_n_type (type, RAW_DATA_LENGTH (elt)); + tree alias_set + = build_int_cst (build_pointer_type (type), 0); + tree lhs = build2 (MEM_REF, atype, base, alias_set); + tree ctor + = build_constructor_single (atype, bitsize_zero_node, + copy_node (elt)); + one_init = build2 (MODIFY_EXPR, void_type_node, lhs, ctor); + + if (try_const) + { + if (!field) + field = size_int (num_initialized_elts); + CONSTRUCTOR_APPEND_ELT (const_vec, field, elt); + if (do_static_init) + one_init = NULL_TREE; + } + + if (one_init) + finish_expr_stmt (one_init); + + /* Adjust the counter and pointer. */ + tree length = build_int_cst (ptrdiff_type_node, + RAW_DATA_LENGTH (elt)); + one_init = cp_build_binary_op (loc, MINUS_EXPR, iterator, + length, complain); + gcc_assert (one_init != error_mark_node); + one_init = build2 (MODIFY_EXPR, void_type_node, iterator, + one_init); + finish_expr_stmt (one_init); + + one_init = cp_build_binary_op (loc, PLUS_EXPR, base, length, + complain); + gcc_assert (one_init != error_mark_node); + one_init = build2 (MODIFY_EXPR, void_type_node, base, + one_init); + finish_expr_stmt (one_init); + + num_initialized_elts += RAW_DATA_LENGTH (elt); + continue; + } + else + { + /* Otherwise peel it off into separate constants. */ + tree orig_elt = elt; + elt = build_int_cst (TREE_TYPE (elt), + RAW_DATA_UCHAR_ELT (elt, ++raw_idx)); + if (raw_idx && field) + field = size_binop (PLUS_EXPR, field, + bitsize_int (raw_idx)); + if (raw_idx + 1 == (unsigned) RAW_DATA_LENGTH (orig_elt)) + raw_idx = -1; + else + --idx; + } + } + + tree baseref = build1 (INDIRECT_REF, type, base); /* We need to see sub-array TARGET_EXPR before cp_fold_r so we can handle cleanup flags properly. */ @@ -4919,7 +5004,7 @@ build_vec_init (tree base, tree maxindex, tree init, if (try_const) { if (!field) - field = size_int (idx); + field = size_int (num_initialized_elts); tree e = maybe_constant_init (one_init); if (reduced_constant_expression_p (e)) { @@ -4942,6 +5027,21 @@ build_vec_init (tree base, tree maxindex, tree init, } } + tree end = NULL_TREE, body = NULL_TREE; + if (field && TREE_CODE (field) == RANGE_EXPR) + { + tree sub + = cp_build_binary_op (loc, MINUS_EXPR, iterator, + build_int_cst (TREE_TYPE (iterator), + range_expr_nelts (field)), + complain); + gcc_assert (sub != error_mark_node); + end = get_internal_target_expr (sub); + add_stmt (end); + + body = push_stmt_list (); + } + if (one_init) { /* Only create one std::allocator temporary. */ @@ -4962,6 +5062,20 @@ build_vec_init (tree base, tree maxindex, tree init, errors = true; else finish_expr_stmt (one_init); + + if (field && TREE_CODE (field) == RANGE_EXPR) + { + tree exit = build1 (EXIT_EXPR, void_type_node, + build2 (EQ_EXPR, boolean_type_node, + iterator, TARGET_EXPR_SLOT (end))); + add_stmt (exit); + body = pop_stmt_list (body); + tree loop = build1 (LOOP_EXPR, void_type_node, body); + add_stmt (loop); + num_initialized_elts += range_expr_nelts (field); + } + else + num_initialized_elts++; } /* Any elements without explicit initializers get T{}. */ diff --git a/gcc/testsuite/g++.dg/cpp/embed-29.C b/gcc/testsuite/g++.dg/cpp/embed-29.C new file mode 100644 index 000000000000..f1a0b8191439 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp/embed-29.C @@ -0,0 +1,24 @@ +// PR c++/124531 +// { dg-do run { target c++11 } } +// { dg-options "-O2 --embed-dir=${srcdir}/c-c++-common/cpp/embed-dir" } + +unsigned char * +foo () +{ + return new unsigned char [] { +#embed <magna-carta.txt> limit (231) + }; +} + +int +main () +{ + unsigned char *p = foo (); + static unsigned char q[] = { +#embed <magna-carta.txt> limit (231) + }; + for (int i = 0; i < 231; ++i) + if (p[i] != q[i]) + __builtin_abort (); + delete [] p; +} diff --git a/gcc/testsuite/g++.dg/cpp0x/pr124531.C b/gcc/testsuite/g++.dg/cpp0x/pr124531.C new file mode 100644 index 000000000000..3a6e1aa42fed --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/pr124531.C @@ -0,0 +1,56 @@ +// PR c++/124531 +// { dg-do run { target c++11 } } +// { dg-options "-O2" } + +unsigned char * +foo () +{ + return new unsigned char [] { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 }; +} + +struct R +{ + int a, b; + constexpr R () : a (0), b (1) { } + constexpr R (int x, int y) : a (x), b (y) {} +}; + +R * +bar (int *p) +{ + return new R[64] { R (++*p, 43) }; +} + +int +main () +{ + unsigned char *p = foo (); + for (int i = 0; i < 256; ++i) + if (p[i] != i) + __builtin_abort (); + delete [] p; + int x = 41; + R *q = bar (&x); + if (x != 42 || q[0].a != 42 || q[0].b != 43) + __builtin_abort (); + for (int i = 1; i < 64; ++i) + if (q[i].a != 0 || q[i].b != 1) + __builtin_abort (); + delete [] q; +}
