On Fri, Jun 2, 2023 at 3:32 AM Jason Merrill via Gcc-patches <gcc-patches@gcc.gnu.org> wrote: > > I ended up deciding not to apply the DECL_NOT_OBSERVABLE patch that you > approved in stage 3 because I didn't feel like it was fully baked; I'm happy > with this version now, which seems like a more broadly useful flag. > > Tested x86_64-pc-linux-gnu. OK for trunk?
OK. Richard. > -- 8< -- > > After the maybe_init_list_as_* patches, I noticed that we were putting the > array of strings into .rodata, but then memcpying it into an automatic > array, which is pointless; we should be able to use it directly. > > This doesn't happen automatically because TREE_ADDRESSABLE is set (since > r12-657 for PR100464), and so gimplify_init_constructor won't promote the > variable to static. Theoretically we could do escape analysis to recognize > that the address, though taken, never leaves the function; that would allow > promotion when we're only using the address for indexing within the > function, as in initlist-opt2.C. But this would be a new pass. > > And in initlist-opt1.C, we're passing the array address to another function, > so it definitely escapes; it's only safe in this case because it's calling a > standard library function that we know only uses it for indexing. So, a > flag seems needed. I first thought to put the flag on the TARGET_EXPR, but > the VAR_DECL seems more appropriate. > > In a previous revision of the patch I called this flag DECL_NOT_OBSERVABLE, > but I think DECL_MERGEABLE is a better name, especially if we're going to > apply it to the backing array of initializer_list, which is observable. I > then also check it in places that check for -fmerge-all-constants, so that > multiple equivalent initializer-lists can also be combined. And then it > seemed to make sense for [[no_unique_address]] to have this meaning for > user-written variables. > > I think the note in [dcl.init.list]/6 intended to allow this kind of merging > for initializer_lists, but it didn't actually work; for an explicit array > with the same initializer, if the address escapes the program could tell > whether the same variable in two frames have the same address. P2752 is > trying to correct this defect, so I'm going to assume that this is the > intent. > > PR c++/110070 > PR c++/105838 > > gcc/ChangeLog: > > * tree.h (DECL_MERGEABLE): New. > * tree-core.h (struct tree_decl_common): Mention it. > * gimplify.cc (gimplify_init_constructor): Check it. > * cgraph.cc (symtab_node::address_can_be_compared_p): Likewise. > * varasm.cc (categorize_decl_for_section): Likewise. > > gcc/cp/ChangeLog: > > * call.cc (maybe_init_list_as_array): Set DECL_MERGEABLE. > (convert_like_internal) [ck_list]: Set it. > (set_up_extended_ref_temp): Copy it. > * tree.cc (handle_no_unique_addr_attribute): Set it. > > gcc/testsuite/ChangeLog: > > * g++.dg/tree-ssa/initlist-opt1.C: Check for static array. > * g++.dg/tree-ssa/initlist-opt2.C: Likewise. > * g++.dg/tree-ssa/initlist-opt4.C: New test. > * g++.dg/opt/icf1.C: New test. > * g++.dg/opt/icf2.C: New test. > --- > gcc/tree-core.h | 3 ++- > gcc/tree.h | 6 ++++++ > gcc/cgraph.cc | 2 +- > gcc/cp/call.cc | 15 ++++++++++++--- > gcc/cp/tree.cc | 9 ++++++++- > gcc/gimplify.cc | 3 ++- > gcc/testsuite/g++.dg/opt/icf1.C | 16 ++++++++++++++++ > gcc/testsuite/g++.dg/opt/icf2.C | 17 +++++++++++++++++ > gcc/testsuite/g++.dg/tree-ssa/initlist-opt1.C | 1 + > gcc/testsuite/g++.dg/tree-ssa/initlist-opt2.C | 2 ++ > gcc/testsuite/g++.dg/tree-ssa/initlist-opt4.C | 13 +++++++++++++ > gcc/varasm.cc | 2 +- > 12 files changed, 81 insertions(+), 8 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/opt/icf1.C > create mode 100644 gcc/testsuite/g++.dg/opt/icf2.C > create mode 100644 gcc/testsuite/g++.dg/tree-ssa/initlist-opt4.C > > diff --git a/gcc/tree-core.h b/gcc/tree-core.h > index 9d44c04bf03..6dd7b680b57 100644 > --- a/gcc/tree-core.h > +++ b/gcc/tree-core.h > @@ -1803,7 +1803,8 @@ struct GTY(()) tree_decl_common { > In VAR_DECL, PARM_DECL and RESULT_DECL, this is > DECL_HAS_VALUE_EXPR_P. */ > unsigned decl_flag_2 : 1; > - /* In FIELD_DECL, this is DECL_PADDING_P. */ > + /* In FIELD_DECL, this is DECL_PADDING_P. > + In VAR_DECL, this is DECL_MERGEABLE. */ > unsigned decl_flag_3 : 1; > /* Logically, these two would go in a theoretical base shared by var and > parm decl. */ > diff --git a/gcc/tree.h b/gcc/tree.h > index 0b72663e6a1..8a4beba1230 100644 > --- a/gcc/tree.h > +++ b/gcc/tree.h > @@ -3233,6 +3233,12 @@ extern void decl_fini_priority_insert (tree, > priority_type); > #define DECL_NONALIASED(NODE) \ > (VAR_DECL_CHECK (NODE)->base.nothrow_flag) > > +/* In a VAR_DECL, nonzero if this variable is not required to have a distinct > + address from other variables with the same constant value. In other > words, > + consider -fmerge-all-constants to be on for this VAR_DECL. */ > +#define DECL_MERGEABLE(NODE) \ > + (VAR_DECL_CHECK (NODE)->decl_common.decl_flag_3) > + > /* This field is used to reference anything in decl.result and is meant only > for use by the garbage collector. */ > #define DECL_RESULT_FLD(NODE) \ > diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc > index e8f9bec8227..e41e5ad3ae7 100644 > --- a/gcc/cgraph.cc > +++ b/gcc/cgraph.cc > @@ -158,7 +158,7 @@ symtab_node::address_can_be_compared_p () > flag_merge_constants permits us to assume the same on readonly vars. */ > if (is_a <varpool_node *> (this) > && (DECL_IN_CONSTANT_POOL (decl) > - || (flag_merge_constants >= 2 > + || ((flag_merge_constants >= 2 || DECL_MERGEABLE (decl)) > && TREE_READONLY (decl) && !TREE_THIS_VOLATILE (decl)))) > return false; > return true; > diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc > index 6c36c755481..d6154f1a319 100644 > --- a/gcc/cp/call.cc > +++ b/gcc/cp/call.cc > @@ -4274,7 +4274,9 @@ maybe_init_list_as_array (tree elttype, tree init) > > init_elttype = cp_build_qualified_type (init_elttype, TYPE_QUAL_CONST); > tree arr = build_array_of_n_type (init_elttype, CONSTRUCTOR_NELTS (init)); > - return finish_compound_literal (arr, init, tf_none); > + arr = finish_compound_literal (arr, init, tf_none); > + DECL_MERGEABLE (TARGET_EXPR_SLOT (arr)) = true; > + return arr; > } > > /* If we were going to call e.g. vector(initializer_list<string>) starting > @@ -8558,6 +8560,8 @@ convert_like_internal (conversion *convs, tree expr, > tree fn, int argnum, > (elttype, cp_type_quals (elttype) | TYPE_QUAL_CONST); > array = build_array_of_n_type (elttype, len); > array = finish_compound_literal (array, new_ctor, complain); > + /* This is dubious now, should be blessed by P2752. */ > + DECL_MERGEABLE (TARGET_EXPR_SLOT (array)) = true; > /* Take the address explicitly rather than via decay_conversion > to avoid the error about taking the address of a temporary. */ > array = cp_build_addr_expr (array, complain); > @@ -13602,8 +13606,13 @@ set_up_extended_ref_temp (tree decl, tree expr, > vec<tree, va_gc> **cleanups, > VAR. */ > if (TREE_CODE (expr) != TARGET_EXPR) > expr = get_target_expr (expr); > - else if (TREE_ADDRESSABLE (expr)) > - TREE_ADDRESSABLE (var) = 1; > + else > + { > + if (TREE_ADDRESSABLE (expr)) > + TREE_ADDRESSABLE (var) = 1; > + if (DECL_MERGEABLE (TARGET_EXPR_SLOT (expr))) > + DECL_MERGEABLE (var) = true; > + } > > if (TREE_CODE (decl) == FIELD_DECL > && extra_warnings && !warning_suppressed_p (decl)) > diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc > index 19dfb3ed782..4d5e3f53c5e 100644 > --- a/gcc/cp/tree.cc > +++ b/gcc/cp/tree.cc > @@ -5045,7 +5045,14 @@ handle_no_unique_addr_attribute (tree* node, > int /*flags*/, > bool* no_add_attrs) > { > - if (TREE_CODE (*node) != FIELD_DECL) > + if (TREE_CODE (*node) == VAR_DECL) > + { > + DECL_MERGEABLE (*node) = true; > + if (pedantic) > + warning (OPT_Wattributes, "%qE attribute can only be applied to " > + "non-static data members", name); > + } > + else if (TREE_CODE (*node) != FIELD_DECL) > { > warning (OPT_Wattributes, "%qE attribute can only be applied to " > "non-static data members", name); > diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc > index d0d16a24820..bd5d0bf2bd8 100644 > --- a/gcc/gimplify.cc > +++ b/gcc/gimplify.cc > @@ -5253,7 +5253,8 @@ gimplify_init_constructor (tree *expr_p, gimple_seq > *pre_p, gimple_seq *post_p, > && TREE_READONLY (object) > && VAR_P (object) > && !DECL_REGISTER (object) > - && (flag_merge_constants >= 2 || !TREE_ADDRESSABLE (object)) > + && (flag_merge_constants >= 2 || !TREE_ADDRESSABLE (object) > + || DECL_MERGEABLE (object)) > /* For ctors that have many repeated nonzero elements > represented through RANGE_EXPRs, prefer initializing > those through runtime loops over copies of large amounts > diff --git a/gcc/testsuite/g++.dg/opt/icf1.C b/gcc/testsuite/g++.dg/opt/icf1.C > new file mode 100644 > index 00000000000..fbb275e635a > --- /dev/null > +++ b/gcc/testsuite/g++.dg/opt/icf1.C > @@ -0,0 +1,16 @@ > +// Test that -fipa-icf combines i and j. > +// { dg-do run { target c++11 } } > +// { dg-options -fipa-icf } > + > +[[no_unique_address]] extern const int i[] = { 1,2,3 }; > +[[no_unique_address]] extern const int j[] = { 1,2,3 }; > + > +[[gnu::noipa]] void f (const void *a, const void *b) > +{ > + if (a != b) __builtin_abort(); > +} > + > +int main() > +{ > + f (&i, &j); > +} > diff --git a/gcc/testsuite/g++.dg/opt/icf2.C b/gcc/testsuite/g++.dg/opt/icf2.C > new file mode 100644 > index 00000000000..1ad48f6173d > --- /dev/null > +++ b/gcc/testsuite/g++.dg/opt/icf2.C > @@ -0,0 +1,17 @@ > +// Test that -fipa-icf combines the backing arrays for a and b. > +// { dg-do run { target c++11 } } > +// { dg-options -fipa-icf } > + > +#include <initializer_list> > + > +[[gnu::noipa]] void f (const void *a, const void *b) > +{ > + if (a != b) __builtin_abort(); > +} > + > +int main() > +{ > + auto a = { 1, 2 }; > + auto b = { 1, 2 }; > + f (a.begin(), b.begin()); > +} > diff --git a/gcc/testsuite/g++.dg/tree-ssa/initlist-opt1.C > b/gcc/testsuite/g++.dg/tree-ssa/initlist-opt1.C > index 053317b59d8..b1d2d25faf4 100644 > --- a/gcc/testsuite/g++.dg/tree-ssa/initlist-opt1.C > +++ b/gcc/testsuite/g++.dg/tree-ssa/initlist-opt1.C > @@ -4,6 +4,7 @@ > > // Test that we do range-initialization from const char *. > // { dg-final { scan-tree-dump {_M_range_initialize<const char\* const\*>} > "gimple" } } > +// { dg-final { scan-tree-dump {static const char.*72} "gimple" } } > > #include <string> > #include <vector> > diff --git a/gcc/testsuite/g++.dg/tree-ssa/initlist-opt2.C > b/gcc/testsuite/g++.dg/tree-ssa/initlist-opt2.C > index c20713afc6a..1e9ac739b2d 100644 > --- a/gcc/testsuite/g++.dg/tree-ssa/initlist-opt2.C > +++ b/gcc/testsuite/g++.dg/tree-ssa/initlist-opt2.C > @@ -4,6 +4,8 @@ > > // Test that we do range-initialization from const char *. > // { dg-final { scan-tree-dump {_M_range_initialize<const char\* const\*>} > "gimple" } } > +// And that the backing array is static. > +// { dg-final { scan-tree-dump {static const char.*72} "gimple" } } > > #include <string> > #include <vector> > diff --git a/gcc/testsuite/g++.dg/tree-ssa/initlist-opt4.C > b/gcc/testsuite/g++.dg/tree-ssa/initlist-opt4.C > new file mode 100644 > index 00000000000..16e2e53c720 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/tree-ssa/initlist-opt4.C > @@ -0,0 +1,13 @@ > +// PR c++/110070 > +// { dg-additional-options -fdump-tree-gimple } > +// { dg-do compile { target c++11 } } > + > +// { dg-final { scan-tree-dump {static const int [^\n]*\[4\] = } "gimple" } } > + > +#include <initializer_list> > +extern void ext(int); > +void foo() > +{ > + for (int i: {1,2,4,6}) > + ext(i); > +} > diff --git a/gcc/varasm.cc b/gcc/varasm.cc > index 9552f90f923..b66d1f86bb3 100644 > --- a/gcc/varasm.cc > +++ b/gcc/varasm.cc > @@ -7032,7 +7032,7 @@ categorize_decl_for_section (const_tree decl, int reloc) > } > else if (reloc & targetm.asm_out.reloc_rw_mask ()) > ret = reloc == 1 ? SECCAT_DATA_REL_RO_LOCAL : SECCAT_DATA_REL_RO; > - else if (reloc || flag_merge_constants < 2 > + else if (reloc || (flag_merge_constants < 2 && !DECL_MERGEABLE (decl)) > || ((flag_sanitize & SANITIZE_ADDRESS) > /* PR 81697: for architectures that use section anchors we > need to ignore DECL_RTL_SET_P (decl) for string > constants > > base-commit: 5fccebdbd9666e0adf6dd8357c21d4ef3ac3f83f > -- > 2.31.1 >