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
>

Reply via email to