diff mbox series

c++: cross-module __cxa_atexit use [PR 98531]

Message ID 1d8a0534-8204-97d3-b2ee-a5260fc4dde2@acm.org
State New
Headers show
Series c++: cross-module __cxa_atexit use [PR 98531] | expand

Commit Message

Nathan Sidwell Jan. 25, 2021, 6:01 p.m. UTC
[ARM has unique ABI here, hence cc'ing Richard]

Solaris tickled this bug as it has some mutex/sync/something primitive 
with a destructor, hence wanted to generate a __cxa_atexit call inside 
an inline/template function.  But the problem is not solaris-specific.

I tested this bootstrapping both x86_64-linux and aarch64-linux.  I'll 
commit in a couple of days if there are no further comments.

The compiler's use of lazily-declared library functions must insert
said functions into a symbol table, so that they can be correctly
merged across TUs at the module-level.  We have too many different
ways of declaring such library functions.  This fixes __cxa_atexit (or
its system-specific variations), pushing (or merging) the decl into
the appropriate namespace.

             PR c++/98531
             gcc/cp/
             * cp-tree.h (push_abi_namespace, pop_abi_namespace): Declare.
             * decl.c (push_abi_namespace, pop_abi_namespace): Moved
             from rtti.c, add default namespace arg.
             (declare_global_var): Use push/pop_abi_namespace.
             (get_atexit_node): Push the fndecl into a namespace.
             * rtti.c (push_abi_namespace, pop_abi_namespace): Moved to
             decl.c.
             gcc/testsuite/
             * g++.dg/modules/pr98531.h: New.
             * g++.dg/modules/pr98531_a.H: New.
             * g++.dg/modules/pr98531_b.C: New.

Comments

Rainer Orth Jan. 27, 2021, 1:31 p.m. UTC | #1
Hi Nathan,

> Solaris tickled this bug as it has some mutex/sync/something primitive with
> a destructor, hence wanted to generate a __cxa_atexit call inside an
> inline/template function.  But the problem is not solaris-specific.
>
> I tested this bootstrapping both x86_64-linux and aarch64-linux.  I'll
> commit in a couple of days if there are no further comments.

I've tried the patch on Solaris last night, with mixed results (some
regressions, remaining failures).  I've reported the details in the PR.

	Rainer
Nathan Sidwell Jan. 29, 2021, 5:22 p.m. UTC | #2
As Rainer pointed out, there were some regressions in the library tests. 
  That's because we didn't build the correct ehspec for __cxa_atexit. 
This adds that, but also, I realize we can use the 'hidden' flag on 
pushdecl to make this lazy-builtin not visible to user name lookup 
without them already declaring it.  And I realized we should be setting 
the location as BUILTIN_LOCATION because that's what we're doing. 
Finally the duplicate_decl processing needed augmenting to allow such a 
lazy builtin's eh spec to differ from one already known.  (The converse 
is already dealt with, as that's the only case we had before.)

As I mentioned in the first patch, a cleanup of the 'lazily declare a 
builtin' API would be a nice stage-1 thing.

I'll hold off applying this until next week.  Rainer, I don't yet know 
if this resolves all of the issues you encountered in the PR.

On 1/25/21 1:01 PM, Nathan Sidwell wrote:
> [ARM has unique ABI here, hence cc'ing Richard]
> 
> Solaris tickled this bug as it has some mutex/sync/something primitive 
> with a destructor, hence wanted to generate a __cxa_atexit call inside 
> an inline/template function.  But the problem is not solaris-specific.
> 
> I tested this bootstrapping both x86_64-linux and aarch64-linux.  I'll 
> commit in a couple of days if there are no further comments.
> 
> The compiler's use of lazily-declared library functions must insert
> said functions into a symbol table, so that they can be correctly
> merged across TUs at the module-level.  We have too many different
> ways of declaring such library functions.  This fixes __cxa_atexit (or
> its system-specific variations), pushing (or merging) the decl into
> the appropriate namespace.
> 
>              PR c++/98531
>              gcc/cp/
>              * cp-tree.h (push_abi_namespace, pop_abi_namespace): Declare.
>              * decl.c (push_abi_namespace, pop_abi_namespace): Moved
>              from rtti.c, add default namespace arg.
>              (declare_global_var): Use push/pop_abi_namespace.
>              (get_atexit_node): Push the fndecl into a namespace.
>              * rtti.c (push_abi_namespace, pop_abi_namespace): Moved to
>              decl.c.
>              gcc/testsuite/
>              * g++.dg/modules/pr98531.h: New.
>              * g++.dg/modules/pr98531_a.H: New.
>              * g++.dg/modules/pr98531_b.C: New.
>
Rainer Orth Feb. 1, 2021, 12:39 p.m. UTC | #3
Hi Nathan,

> As Rainer pointed out, there were some regressions in the library tests.
> That's because we didn't build the correct ehspec for __cxa_atexit. 
> This adds that, but also, I realize we can use the 'hidden' flag on
> pushdecl to make this lazy-builtin not visible to user name lookup 
> without them already declaring it.  And I realized we should be setting the
> location as BUILTIN_LOCATION because that's what we're doing. Finally the
> duplicate_decl processing needed augmenting to allow such a lazy builtin's
> eh spec to differ from one already known.  (The converse is already dealt
> with, as that's the only case we had before.)
>
> As I mentioned in the first patch, a cleanup of the 'lazily declare a
> builtin' API would be a nice stage-1 thing.
>
> I'll hold off applying this until next week.  Rainer, I don't yet know if
> this resolves all of the issues you encountered in the PR.

unfortunately, the results are not very encouraging: while the new
libstdc++ failures caused by the v1 patch are gone, the g++.dg/modules
ICEs remain unchanged, and on Solaris 11.3 (or with
-fno-use-cxa-atexit), the new tests ICE, too.  Full details in the PR.

	Rainer
Nathan Sidwell Feb. 3, 2021, 12:59 p.m. UTC | #4
[Dropping Richard, as I don't think his input is necessary further]

Hi Rainer,
here are two patches, to be applied in sequence (replacing earlier 
versions of the patch).

The first is merely a testsuite correction of the earlier patch to 
inhibit the cxa_atexit-specific tests where it matters.  (I don't think 
it matters in the ABI ones, if the library does not provide them, we 
won't hit a declaration that could differ)

The second fixes the atexit issue (and array entity with cxa_atexit) you 
encountered on solaris 11.3.

With these two patches I could build the xtreme-header-2_a.ii you 
provided on an i386-solaris2.11 xcompiler (I can only compile there, not 
got a complete runtime to run the tests).

Please let me know how these behave, thanks.

patch 3a:
     c++: cross-module __cxa_atexit use [PR 98531]

The compiler's use of lazily-declared library functions must insert
said functions into a symbol table, so that they can be correctly
merged across TUs at the module-level.  We have too many different
ways of declaring such library functions.  This fixes __cxa_atexit (or
its system-specific variations), pushing (or merging) the decl into
the appropriate namespace.  Because we're pushing a lazy builtin,
check_redeclaration_exception_specification needed a tweak to allow a
such a builtin's eh spec to differ from what the user may have already
declared. (I suspect no all headers declare atexit as noexcept.)

We can't test the -fno-use-cxa-atexit path with modules, as that
requires a followup patch to a closely related piece (which also
affects cxa_atexit targets in other circumstances).

             PR c++/98531
             gcc/cp/
             * cp-tree.h (push_abi_namespace, pop_abi_namespace): Declare.
             * decl.c (push_abi_namespace, pop_abi_namespace): Moved
             from rtti.c, add default namespace arg.
             (check_redeclaration_exception_specification): Allow a lazy
             builtin's eh spec to differ from an lready-declared user
             declaration.
             (declare_global_var): Use push/pop_abi_namespace.
             (get_atexit_node): Push the fndecl into a namespace.
             * rtti.c (push_abi_namespace, pop_abi_namespace): Moved to
             decl.c.
             gcc/testsuite/
             * g++.dg/modules/pr98531-1.h: New.
             * g++.dg/modules/pr98531-1_a.H: New.
             * g++.dg/modules/pr98531-1_b.C: New.
             * g++.dg/abi/pr98531-1.C: New.
             * g++.dg/abi/pr98531-2.C: New.
             * g++.dg/abi/pr98531-3.C: New.
             * g++.dg/abi/pr98531-4.C: New.


patch 3b:
c++: cleanup function name [PR 98531]

The next piece of 98531 is that in some cases we need to create a
cleanup function to do the work (when the object is an array, or we're
using regular atexit).  We were not pushing that function's decl
anywhere (not giving it a context) so streaming it failed.

This is a partial fix.  You'll notice we're naming these from a per-TU
counter.  I've captured that in PR98893.

             gcc/cp/
             * decl.c (start_cleanup_fn): Push function into
             namespace.
             gcc/testsuite/
             * g++.dg/modules/pr98531-2.h: New.
             * g++.dg/modules/pr98531-2_a.H: New.
             * g++.dg/modules/pr98531-2_b.C: New.
             * g++.dg/modules/pr98531-3.h: New.
             * g++.dg/modules/pr98531-3_a.H: New.
             * g++.dg/modules/pr98531-3_b.C: New.
diff mbox series

Patch

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 51139f4a4be..fefd870360b 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -193,7 +193,9 @@  enum cp_tree_index
 
     CPTI_MODULE_HWM,
     /* Nodes after here change during compilation, or should not be in
-       the module's global tree table.  */
+       the module's global tree table.  Such nodes must be locatable
+       via name lookup or type-construction, as those are the only
+       cross-TU matching capabilities remaining.  */
 
     /* We must find these via the global namespace.  */
     CPTI_STD,
@@ -6620,6 +6622,9 @@  extern tree make_typename_type			(tree, tree, enum tag_types, tsubst_flags_t);
 extern tree build_typename_type			(tree, tree, tree, tag_types);
 extern tree make_unbound_class_template		(tree, tree, tree, tsubst_flags_t);
 extern tree make_unbound_class_template_raw	(tree, tree, tree);
+extern unsigned push_abi_namespace		(tree node = abi_node);
+extern void pop_abi_namespace			(unsigned flags,
+						 tree node = abi_node);
 extern tree build_library_fn_ptr		(const char *, tree, int);
 extern tree build_cp_library_fn_ptr		(const char *, tree, int);
 extern tree push_library_fn			(tree, tree, tree, int);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 1a114a2e2d0..1f97bcf1e55 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -4696,6 +4696,30 @@  cxx_init_decl_processing (void)
     using_eh_for_cleanups ();
 }
 
+/* Enter an abi node in global-module context.  returns a cookie to
+   give to pop_abi_namespace.  */
+
+unsigned
+push_abi_namespace (tree node)
+{
+  push_nested_namespace (node);
+  push_visibility ("default", 2);
+  unsigned flags = module_kind;
+  module_kind = 0;
+  return flags;
+}
+
+/* Pop an abi namespace, FLAGS is the cookie push_abi_namespace gave
+   you.  */
+
+void
+pop_abi_namespace (unsigned flags, tree node)
+{
+  module_kind = flags;
+  pop_visibility (2);
+  pop_nested_namespace (node);
+}
+
 /* Create the VAR_DECL for __FUNCTION__ etc. ID is the name to give
    the decl, LOC is the location to give the decl, NAME is the
    initialization string and TYPE_DEP indicates whether NAME depended
@@ -8668,21 +8692,19 @@  cp_finish_decomp (tree decl, tree first, unsigned int count)
 static tree
 declare_global_var (tree name, tree type)
 {
-  tree decl;
-
-  push_to_top_level ();
-  decl = build_decl (input_location, VAR_DECL, name, type);
+  auto cookie = push_abi_namespace (global_namespace);
+  tree decl = build_decl (input_location, VAR_DECL, name, type);
   TREE_PUBLIC (decl) = 1;
   DECL_EXTERNAL (decl) = 1;
   DECL_ARTIFICIAL (decl) = 1;
-  DECL_CONTEXT (decl) = FROB_CONTEXT (global_namespace);
+  DECL_CONTEXT (decl) = FROB_CONTEXT (current_namespace);
   /* If the user has explicitly declared this variable (perhaps
      because the code we are compiling is part of a low-level runtime
      library), then it is possible that our declaration will be merged
      with theirs by pushdecl.  */
   decl = pushdecl (decl);
   cp_finish_decl (decl, NULL_TREE, false, NULL_TREE, 0);
-  pop_from_top_level ();
+  pop_abi_namespace (cookie, global_namespace);
 
   return decl;
 }
@@ -8727,6 +8749,7 @@  get_atexit_node (void)
   tree fn_ptr_type;
   const char *name;
   bool use_aeabi_atexit;
+  tree ctx = global_namespace;
 
   if (atexit_node)
     return atexit_node;
@@ -8762,9 +8785,20 @@  get_atexit_node (void)
 					  argtype0, argtype1, argtype2,
 					  NULL_TREE);
       if (use_aeabi_atexit)
-	name = "__aeabi_atexit";
+	{
+	  name = "__aeabi_atexit";
+	  push_to_top_level ();
+	  int n = push_namespace (get_identifier ("__aeabiv1"), false);
+	  ctx = current_namespace;
+	  while (n--)
+	    pop_namespace ();
+	  pop_from_top_level ();
+	}
       else
-	name = "__cxa_atexit";
+	{
+	  name = "__cxa_atexit";
+	  ctx = abi_node;
+	}
     }
   else
     {
@@ -8783,7 +8817,11 @@  get_atexit_node (void)
 
   /* Now, build the function declaration.  */
   push_lang_context (lang_name_c);
+  auto cookie = push_abi_namespace (ctx);
   atexit_fndecl = build_library_fn_ptr (name, fn_type, ECF_LEAF | ECF_NOTHROW);
+  DECL_CONTEXT (atexit_fndecl) = FROB_CONTEXT (current_namespace);
+  atexit_fndecl = pushdecl (atexit_fndecl);
+  pop_abi_namespace (cookie, ctx);
   mark_used (atexit_fndecl);
   pop_lang_context ();
   atexit_node = decay_conversion (atexit_fndecl, tf_warning_or_error);
diff --git a/gcc/cp/rtti.c b/gcc/cp/rtti.c
index d3cb9ad4425..b41d95469c6 100644
--- a/gcc/cp/rtti.c
+++ b/gcc/cp/rtti.c
@@ -143,24 +143,6 @@  static bool typeinfo_in_lib_p (tree);
 
 static int doing_runtime = 0;
 
-static unsigned
-push_abi_namespace (void)
-{
-  push_nested_namespace (abi_node);
-  push_visibility ("default", 2);
-  unsigned flags = module_kind;
-  module_kind = 0;
-  return flags;
-}
-
-static void
-pop_abi_namespace (unsigned flags)
-{
-  module_kind = flags;
-  pop_visibility (2);
-  pop_nested_namespace (abi_node);
-}
-
 /* Declare language defined type_info type and a pointer to const
    type_info.  This is incomplete here, and will be completed when
    the user #includes <typeinfo>.  There are language defined
diff --git a/gcc/testsuite/g++.dg/modules/pr98531.h b/gcc/testsuite/g++.dg/modules/pr98531.h
new file mode 100644
index 00000000000..62d4c1d1f90
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/pr98531.h
@@ -0,0 +1,13 @@ 
+
+struct __waiters
+{
+  __waiters() noexcept;
+  ~__waiters () noexcept;
+
+  static __waiters &_S_for()
+  {
+    static __waiters w;
+    
+    return w;
+  }
+};
diff --git a/gcc/testsuite/g++.dg/modules/pr98531_a.H b/gcc/testsuite/g++.dg/modules/pr98531_a.H
new file mode 100644
index 00000000000..81fd00fb6a5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/pr98531_a.H
@@ -0,0 +1,5 @@ 
+// { dg-additional-options -fmodule-header }
+// PR c++ 98531  no-context __cxa_atexit
+// { dg-module-cmi {} }
+
+#include "pr98531.h"
diff --git a/gcc/testsuite/g++.dg/modules/pr98531_b.C b/gcc/testsuite/g++.dg/modules/pr98531_b.C
new file mode 100644
index 00000000000..5b2d96ddfe2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/pr98531_b.C
@@ -0,0 +1,4 @@ 
+// { dg-additional-options "-fmodules-ts -fno-module-lazy" }
+
+#include "pr98531.h"
+import "pr98531_a.H";