Improve TBAA for types in anonymous namespaces
diff mbox series

Message ID 20190718114534.xlzz2csojy5zd43z@kam.mff.cuni.cz
State New
Headers show
Series
  • Improve TBAA for types in anonymous namespaces
Related show

Commit Message

Jan Hubicka July 18, 2019, 11:45 a.m. UTC
Hi,
this patch adjusts LTO tree merging to treat anonymous namespace types
as local to a given TU, so just like !TREE_PUBLIC decls they are not
merged (this is unify_scc change). This makes them to get different
canonical types and act as independent types for TBAA.

I also modified canonical type calculation to never consider anonymous
namespace types as interoperable with structurally equivalent types
declared in non-C++ translation units.

Both these changes are tested by the testcase (first change makes set1
and set2 independent and second chnage makes set1&set2 independent of
set3).

lto-bootstrapped/regtested x86_64-linux
Honza

	* lto-common.c (gimple_register_canonical_type_1): Do not look for
	non-ODR conflicts of types in anonymous namespaces.
	(unify_scc): Do not merge anonymous namespace types.
	* g++.dg/lto/alias-5_0.C: New testcase.
	* g++.dg/lto/alias-5_1.C: New.
	* g++.dg/lto/alias-5_2.c: New.

Comments

Richard Biener July 18, 2019, 12:04 p.m. UTC | #1
On Thu, 18 Jul 2019, Jan Hubicka wrote:

> Hi,
> this patch adjusts LTO tree merging to treat anonymous namespace types
> as local to a given TU, so just like !TREE_PUBLIC decls they are not
> merged (this is unify_scc change). This makes them to get different
> canonical types and act as independent types for TBAA.
> 
> I also modified canonical type calculation to never consider anonymous
> namespace types as interoperable with structurally equivalent types
> declared in non-C++ translation units.
> 
> Both these changes are tested by the testcase (first change makes set1
> and set2 independent and second chnage makes set1&set2 independent of
> set3).
> 
> lto-bootstrapped/regtested x86_64-linux

OK.  I wonder if we can/should carve off some bits to note
type_with_linkage_p and type_in_anonymous_namespace_p in the tree
itself?  At least in type_common there's plenty of bits left.
Not sure how expensive / reliable (non-C++?) those tests otherwise are.

Thanks,
Richard.

> Honza
> 
> 	* lto-common.c (gimple_register_canonical_type_1): Do not look for
> 	non-ODR conflicts of types in anonymous namespaces.
> 	(unify_scc): Do not merge anonymous namespace types.
> 	* g++.dg/lto/alias-5_0.C: New testcase.
> 	* g++.dg/lto/alias-5_1.C: New.
> 	* g++.dg/lto/alias-5_2.c: New.
> Index: lto/lto-common.c
> ===================================================================
> --- lto/lto-common.c	(revision 273551)
> +++ lto/lto-common.c	(working copy)
> @@ -418,13 +418,19 @@ gimple_register_canonical_type_1 (tree t
>    if (RECORD_OR_UNION_TYPE_P (t)
>        && odr_type_p (t) && !odr_type_violation_reported_p (t))
>      {
> -      /* Here we rely on fact that all non-ODR types was inserted into
> -	 canonical type hash and thus we can safely detect conflicts between
> -	 ODR types and interoperable non-ODR types.  */
> -      gcc_checking_assert (type_streaming_finished
> -			   && TYPE_MAIN_VARIANT (t) == t);
> -      slot = htab_find_slot_with_hash (gimple_canonical_types, t, hash,
> -				       NO_INSERT);
> +      /* Anonymous namespace types never conflict with non-C++ types.  */
> +      if (type_with_linkage_p (t) && type_in_anonymous_namespace_p (t))
> +	slot = NULL;
> +      else
> +	{
> +	  /* Here we rely on fact that all non-ODR types was inserted into
> +	     canonical type hash and thus we can safely detect conflicts between
> +	     ODR types and interoperable non-ODR types.  */
> +	  gcc_checking_assert (type_streaming_finished
> +			       && TYPE_MAIN_VARIANT (t) == t);
> +	  slot = htab_find_slot_with_hash (gimple_canonical_types, t, hash,
> +					   NO_INSERT);
> +	}
>        if (slot && !TYPE_CXX_ODR_P (*(tree *)slot))
>  	{
>  	  tree nonodr = *(tree *)slot;
> @@ -1640,11 +1646,14 @@ unify_scc (class data_in *data_in, unsig
>        tree t = streamer_tree_cache_get_tree (cache, from + i);
>        scc->entries[i] = t;
>        /* Do not merge SCCs with local entities inside them.  Also do
> -	 not merge TRANSLATION_UNIT_DECLs.  */
> +	 not merge TRANSLATION_UNIT_DECLs and anonymous namespace types.  */
>        if (TREE_CODE (t) == TRANSLATION_UNIT_DECL
>  	  || (VAR_OR_FUNCTION_DECL_P (t)
>  	      && !(TREE_PUBLIC (t) || DECL_EXTERNAL (t)))
> -	  || TREE_CODE (t) == LABEL_DECL)
> +	  || TREE_CODE (t) == LABEL_DECL
> +	  || (TYPE_P (t)
> +	      && type_with_linkage_p (TYPE_MAIN_VARIANT (t))
> +	      && type_in_anonymous_namespace_p (TYPE_MAIN_VARIANT (t))))
>  	{
>  	  /* Avoid doing any work for these cases and do not worry to
>  	     record the SCCs for further merging.  */
> Index: testsuite/g++.dg/lto/alias-5_0.C
> ===================================================================
> --- testsuite/g++.dg/lto/alias-5_0.C	(nonexistent)
> +++ testsuite/g++.dg/lto/alias-5_0.C	(working copy)
> @@ -0,0 +1,35 @@
> +/* { dg-lto-do run } */
> +/* { dg-lto-options { { -O3 -flto } } } */
> +/* This testcase tests that anonymous namespaces in different TUs are treated
> +   as different types by LTO TBAA and that they never alias with structurally
> +   same C types.  */
> +namespace {
> +  __attribute__((used))
> +  struct a {int a;} *p,**ptr=&p;
> +};
> +void
> +set1()
> +{
> +  *ptr=0;
> +}
> +void
> +get1()
> +{
> +  if (!__builtin_constant_p (*ptr==0))
> +    __builtin_abort ();
> +}
> +extern void set2();
> +extern "C" void set3();
> +int n = 1;
> +int
> +main()
> +{
> +  for (int i = 0; i < n; i++)
> +    {
> +      set1();
> +      set2();
> +      set3();
> +      get1();
> +    }
> +  return 0;
> +}
> Index: testsuite/g++.dg/lto/alias-5_1.C
> ===================================================================
> --- testsuite/g++.dg/lto/alias-5_1.C	(nonexistent)
> +++ testsuite/g++.dg/lto/alias-5_1.C	(working copy)
> @@ -0,0 +1,9 @@
> +namespace {
> +  __attribute__((used))
> +  struct a {int a;} *p,**ptr=&p,q;
> +};
> +void
> +set2()
> +{
> +  *ptr=&q;
> +}
> Index: testsuite/g++.dg/lto/alias-5_2.c
> ===================================================================
> --- testsuite/g++.dg/lto/alias-5_2.c	(nonexistent)
> +++ testsuite/g++.dg/lto/alias-5_2.c	(working copy)
> @@ -0,0 +1,7 @@
> +  __attribute__((used))
> +  struct a {int a;} *p,**ptr=&p,q;
> +void
> +set3()
> +{
> +  *ptr=&q;
> +}
>
Jan Hubicka July 18, 2019, 12:10 p.m. UTC | #2
> On Thu, 18 Jul 2019, Jan Hubicka wrote:
> 
> > Hi,
> > this patch adjusts LTO tree merging to treat anonymous namespace types
> > as local to a given TU, so just like !TREE_PUBLIC decls they are not
> > merged (this is unify_scc change). This makes them to get different
> > canonical types and act as independent types for TBAA.
> > 
> > I also modified canonical type calculation to never consider anonymous
> > namespace types as interoperable with structurally equivalent types
> > declared in non-C++ translation units.
> > 
> > Both these changes are tested by the testcase (first change makes set1
> > and set2 independent and second chnage makes set1&set2 independent of
> > set3).
> > 
> > lto-bootstrapped/regtested x86_64-linux
> 
> OK.  I wonder if we can/should carve off some bits to note
> type_with_linkage_p and type_in_anonymous_namespace_p in the tree
> itself?  At least in type_common there's plenty of bits left.
> Not sure how expensive / reliable (non-C++?) those tests otherwise are.

I was thinking of that too. The pattern matching is somewhat ugly.
It is now sanity checked to non-C++ FEs by verifying that CXX_ODR_P is
set if the test matches, so it seems to work reliably (byt indeed there
was issues until this stage1)

After we dropped -fno-odr-type-merging these predicates are no longer
absolutely terrible though. type_with_linkage_p boils down to testing
that TYPE_NAME is TYPE_DECL and it has ASSEMBLER_NAME set.  Anonymous
namespace just looks for <anon> in the type name.

I plan to do some profiling and we can see how bad are they these days.
Flag would also save the weird set of tests we do in
need_assembler_name_p to not get confused by other languages.

Honza
Jan Hubicka July 18, 2019, 12:14 p.m. UTC | #3
> 
> OK.  I wonder if we can/should carve off some bits to note
> type_with_linkage_p and type_in_anonymous_namespace_p in the tree
> itself?  At least in type_common there's plenty of bits left.
> Not sure how expensive / reliable (non-C++?) those tests otherwise are.

It also makes me wonder if other languages (D, Ada, go, Fortran...) have
concept of anonymous namespace types - that is types that are never
interoperable with types from another translation unit.  That would
justify the extra flag pretty well.

Similarly for types with name mangling defined.  Both these bits can be
made indpendent of C++.

Honza
Ian Lance Taylor July 18, 2019, 2:07 p.m. UTC | #4
Jan Hubicka <hubicka@ucw.cz> writes:

>> 
>> OK.  I wonder if we can/should carve off some bits to note
>> type_with_linkage_p and type_in_anonymous_namespace_p in the tree
>> itself?  At least in type_common there's plenty of bits left.
>> Not sure how expensive / reliable (non-C++?) those tests otherwise are.
>
> It also makes me wonder if other languages (D, Ada, go, Fortran...) have
> concept of anonymous namespace types - that is types that are never
> interoperable with types from another translation unit.  That would
> justify the extra flag pretty well.
>
> Similarly for types with name mangling defined.  Both these bits can be
> made indpendent of C++.

Go has the concept, but it implements it by mangling the names with the
package-path, which is required to be unique within an application (the
package-path is normally the path used to find an import, so it is
inherently unique within a file system).

Ian
Jan Hubicka July 18, 2019, 2:12 p.m. UTC | #5
> Jan Hubicka <hubicka@ucw.cz> writes:
> 
> >> 
> >> OK.  I wonder if we can/should carve off some bits to note
> >> type_with_linkage_p and type_in_anonymous_namespace_p in the tree
> >> itself?  At least in type_common there's plenty of bits left.
> >> Not sure how expensive / reliable (non-C++?) those tests otherwise are.
> >
> > It also makes me wonder if other languages (D, Ada, go, Fortran...) have
> > concept of anonymous namespace types - that is types that are never
> > interoperable with types from another translation unit.  That would
> > justify the extra flag pretty well.
> >
> > Similarly for types with name mangling defined.  Both these bits can be
> > made indpendent of C++.
> 
> Go has the concept, but it implements it by mangling the names with the
> package-path, which is required to be unique within an application (the
> package-path is normally the path used to find an import, so it is
> inherently unique within a file system).

Currently we implement ODR names only for C++.  If Go has similar
concept (i.e. types has mangled names and equal names implies equal
types acros sunits), we may want to implemnt it too and improve TBAA for
go programs..  I wonder is there something I can read about go types and
mangling?

This would be good motivation to make ODR type machinery indepenent of
C++.  Until now it was only used to drive devirtualization (which needs
BINFOs that are not done by go FE either) and produce ODR violation
warnings (that I am not sure if would make sense for go), but with TBAA
I think I can take a look into this.

Honza
> 
> Ian
Ian Lance Taylor July 18, 2019, 8:14 p.m. UTC | #6
Jan Hubicka <hubicka@ucw.cz> writes:

>> >> 
>> >> OK.  I wonder if we can/should carve off some bits to note
>> >> type_with_linkage_p and type_in_anonymous_namespace_p in the tree
>> >> itself?  At least in type_common there's plenty of bits left.
>> >> Not sure how expensive / reliable (non-C++?) those tests otherwise are.
>> >
>> > It also makes me wonder if other languages (D, Ada, go, Fortran...) have
>> > concept of anonymous namespace types - that is types that are never
>> > interoperable with types from another translation unit.  That would
>> > justify the extra flag pretty well.
>> >
>> > Similarly for types with name mangling defined.  Both these bits can be
>> > made indpendent of C++.
>> 
>> Go has the concept, but it implements it by mangling the names with the
>> package-path, which is required to be unique within an application (the
>> package-path is normally the path used to find an import, so it is
>> inherently unique within a file system).
>
> Currently we implement ODR names only for C++.  If Go has similar
> concept (i.e. types has mangled names and equal names implies equal
> types acros sunits), we may want to implemnt it too and improve TBAA for
> go programs..  I wonder is there something I can read about go types and
> mangling?

I don't know that the mangling is documented anywhere.  It's just an
implementation detail.  The basic idea follows from
https://golang.org/ref/spec#Import_declarations which explains how to
refer to identifiers defined in other packages.  There is no requirement
that the identifiers in different packages have unique names.  That is
if packages p1 and p2 both define a type T, then p1.T and p2.T are
different types.

Within the GCC middle-end, names will have the mangling applied, so if
the middle-end sees two types both named p1.T, then they are indeed the
same type.  It may be possible to use this fact for better TBAA when
using LTO.


> This would be good motivation to make ODR type machinery indepenent of
> C++.  Until now it was only used to drive devirtualization (which needs
> BINFOs that are not done by go FE either) and produce ODR violation
> warnings (that I am not sure if would make sense for go), but with TBAA
> I think I can take a look into this.

ODR violations can't arise in Go, since there is no way to give two
distinct types/variables/functions the same mangled name.

Go could in principle benefit from devirtualization optimizations, but
they would look pretty different than they do in C++ and I doubt they
could actually share an implementation.

Ian

Patch
diff mbox series

Index: lto/lto-common.c
===================================================================
--- lto/lto-common.c	(revision 273551)
+++ lto/lto-common.c	(working copy)
@@ -418,13 +418,19 @@  gimple_register_canonical_type_1 (tree t
   if (RECORD_OR_UNION_TYPE_P (t)
       && odr_type_p (t) && !odr_type_violation_reported_p (t))
     {
-      /* Here we rely on fact that all non-ODR types was inserted into
-	 canonical type hash and thus we can safely detect conflicts between
-	 ODR types and interoperable non-ODR types.  */
-      gcc_checking_assert (type_streaming_finished
-			   && TYPE_MAIN_VARIANT (t) == t);
-      slot = htab_find_slot_with_hash (gimple_canonical_types, t, hash,
-				       NO_INSERT);
+      /* Anonymous namespace types never conflict with non-C++ types.  */
+      if (type_with_linkage_p (t) && type_in_anonymous_namespace_p (t))
+	slot = NULL;
+      else
+	{
+	  /* Here we rely on fact that all non-ODR types was inserted into
+	     canonical type hash and thus we can safely detect conflicts between
+	     ODR types and interoperable non-ODR types.  */
+	  gcc_checking_assert (type_streaming_finished
+			       && TYPE_MAIN_VARIANT (t) == t);
+	  slot = htab_find_slot_with_hash (gimple_canonical_types, t, hash,
+					   NO_INSERT);
+	}
       if (slot && !TYPE_CXX_ODR_P (*(tree *)slot))
 	{
 	  tree nonodr = *(tree *)slot;
@@ -1640,11 +1646,14 @@  unify_scc (class data_in *data_in, unsig
       tree t = streamer_tree_cache_get_tree (cache, from + i);
       scc->entries[i] = t;
       /* Do not merge SCCs with local entities inside them.  Also do
-	 not merge TRANSLATION_UNIT_DECLs.  */
+	 not merge TRANSLATION_UNIT_DECLs and anonymous namespace types.  */
       if (TREE_CODE (t) == TRANSLATION_UNIT_DECL
 	  || (VAR_OR_FUNCTION_DECL_P (t)
 	      && !(TREE_PUBLIC (t) || DECL_EXTERNAL (t)))
-	  || TREE_CODE (t) == LABEL_DECL)
+	  || TREE_CODE (t) == LABEL_DECL
+	  || (TYPE_P (t)
+	      && type_with_linkage_p (TYPE_MAIN_VARIANT (t))
+	      && type_in_anonymous_namespace_p (TYPE_MAIN_VARIANT (t))))
 	{
 	  /* Avoid doing any work for these cases and do not worry to
 	     record the SCCs for further merging.  */
Index: testsuite/g++.dg/lto/alias-5_0.C
===================================================================
--- testsuite/g++.dg/lto/alias-5_0.C	(nonexistent)
+++ testsuite/g++.dg/lto/alias-5_0.C	(working copy)
@@ -0,0 +1,35 @@ 
+/* { dg-lto-do run } */
+/* { dg-lto-options { { -O3 -flto } } } */
+/* This testcase tests that anonymous namespaces in different TUs are treated
+   as different types by LTO TBAA and that they never alias with structurally
+   same C types.  */
+namespace {
+  __attribute__((used))
+  struct a {int a;} *p,**ptr=&p;
+};
+void
+set1()
+{
+  *ptr=0;
+}
+void
+get1()
+{
+  if (!__builtin_constant_p (*ptr==0))
+    __builtin_abort ();
+}
+extern void set2();
+extern "C" void set3();
+int n = 1;
+int
+main()
+{
+  for (int i = 0; i < n; i++)
+    {
+      set1();
+      set2();
+      set3();
+      get1();
+    }
+  return 0;
+}
Index: testsuite/g++.dg/lto/alias-5_1.C
===================================================================
--- testsuite/g++.dg/lto/alias-5_1.C	(nonexistent)
+++ testsuite/g++.dg/lto/alias-5_1.C	(working copy)
@@ -0,0 +1,9 @@ 
+namespace {
+  __attribute__((used))
+  struct a {int a;} *p,**ptr=&p,q;
+};
+void
+set2()
+{
+  *ptr=&q;
+}
Index: testsuite/g++.dg/lto/alias-5_2.c
===================================================================
--- testsuite/g++.dg/lto/alias-5_2.c	(nonexistent)
+++ testsuite/g++.dg/lto/alias-5_2.c	(working copy)
@@ -0,0 +1,7 @@ 
+  __attribute__((used))
+  struct a {int a;} *p,**ptr=&p,q;
+void
+set3()
+{
+  *ptr=&q;
+}