diff mbox series

libgcc: Use inline variable instead of function-local static

Message ID 6704f905.170a0220.38d3dc.1276@mx.google.com
State New
Headers show
Series libgcc: Use inline variable instead of function-local static | expand

Commit Message

Nathaniel Shead Oct. 8, 2024, 9:18 a.m. UTC
On Tue, Oct 01, 2024 at 12:36:21PM +0200, Jakub Jelinek wrote:
> On Tue, Oct 01, 2024 at 11:10:03AM +0100, Jonathan Wakely wrote:
> > Let's use an inline variable. A function-local static requires
> > __cxa_guard_acquire, which (for some targets, including the ones
> > affected by this change) uses __gthread_active_p which will
> > recursively re-enter the variable's initialization.
> > 
> > So something like:
> > 
> > #pragma GCC diagnostic push
> > #pragma GCC diagnostic ignored "-Wc++17-extensions"
> > inline volatile int __gthread_active = -1;
> > #pragma GCC diagnostic pop
> 
> Note, only for #ifdef __cplusplus, for C there is no such thing as inline
> variables and in that case it should use
> static volatile int __ghtread_active = -1;
> instead.
> 
> 	Jakub
> 

So something like this maybe; bootstrapped and regtested on
x86_64-pc-linux-gnu and aarch64-unknown-linux-gnu, OK for trunk?

Or maybe it would be clearer to do the #ifdef __cplusplus here locally
rather than introducing a new __GTHREAD_VAR macro?

-- >8 --

Subject: [PATCH] libgcc: Use inline variable instead of function-local static

To support C++ ODR rules but remain compatible with C, I had initially
changed these variables to be function-local statics rather than global
internal-linkage decls.

However, both GCC and Clang support using namespace-scope 'inline'
variables even in pre-C++17 (with a warning), so let's simplify and do
that instead.  For C we instead revert back to using an internal-linkage
variable.

	PR c++/115126

libgcc/ChangeLog:

	* gthr-posix.h (__GTHREAD_VAR): New macro.
	(__gthread_active): Be a possibly-inline variable instead of a
	function.  Ignore any resultant diagnostics.
	(__gthread_trigger): __gthread_active is now a variable.
	(__gthread_active_p): Likewise.

Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
---
 libgcc/gthr-posix.h | 39 +++++++++++++++++++--------------------
 1 file changed, 19 insertions(+), 20 deletions(-)

Comments

Jakub Jelinek Oct. 8, 2024, 9:28 a.m. UTC | #1
On Tue, Oct 08, 2024 at 08:18:56PM +1100, Nathaniel Shead wrote:
> On Tue, Oct 01, 2024 at 12:36:21PM +0200, Jakub Jelinek wrote:
> > On Tue, Oct 01, 2024 at 11:10:03AM +0100, Jonathan Wakely wrote:
> > > Let's use an inline variable. A function-local static requires
> > > __cxa_guard_acquire, which (for some targets, including the ones
> > > affected by this change) uses __gthread_active_p which will
> > > recursively re-enter the variable's initialization.
> > > 
> > > So something like:
> > > 
> > > #pragma GCC diagnostic push
> > > #pragma GCC diagnostic ignored "-Wc++17-extensions"
> > > inline volatile int __gthread_active = -1;
> > > #pragma GCC diagnostic pop
> > 
> > Note, only for #ifdef __cplusplus, for C there is no such thing as inline
> > variables and in that case it should use
> > static volatile int __ghtread_active = -1;
> > instead.
> > 
> > 	Jakub
> > 
> 
> So something like this maybe; bootstrapped and regtested on
> x86_64-pc-linux-gnu and aarch64-unknown-linux-gnu, OK for trunk?
> 
> Or maybe it would be clearer to do the #ifdef __cplusplus here locally
> rather than introducing a new __GTHREAD_VAR macro?

Actually, had a look again at this and I don't see what this is trying to
fix.
A function-local static requires __cxa_guard_acquire only if it needs
dynamic initialization, which is not the case here (the initializers are
= -1).  The non-dynamic ones are just initialized that way without any
__cxa_guard_acquire.

So, the function-local static seems more portable than hoping inline vars
will be supported even for older C++ versions.

	Jakub
Jonathan Wakely Oct. 8, 2024, 9:57 a.m. UTC | #2
On Tue, 8 Oct 2024 at 10:28, Jakub Jelinek <jakub@redhat.com> wrote:
>
> On Tue, Oct 08, 2024 at 08:18:56PM +1100, Nathaniel Shead wrote:
> > On Tue, Oct 01, 2024 at 12:36:21PM +0200, Jakub Jelinek wrote:
> > > On Tue, Oct 01, 2024 at 11:10:03AM +0100, Jonathan Wakely wrote:
> > > > Let's use an inline variable. A function-local static requires
> > > > __cxa_guard_acquire, which (for some targets, including the ones
> > > > affected by this change) uses __gthread_active_p which will
> > > > recursively re-enter the variable's initialization.
> > > >
> > > > So something like:
> > > >
> > > > #pragma GCC diagnostic push
> > > > #pragma GCC diagnostic ignored "-Wc++17-extensions"
> > > > inline volatile int __gthread_active = -1;
> > > > #pragma GCC diagnostic pop
> > >
> > > Note, only for #ifdef __cplusplus, for C there is no such thing as inline
> > > variables and in that case it should use
> > > static volatile int __ghtread_active = -1;
> > > instead.
> > >
> > >     Jakub
> > >
> >
> > So something like this maybe; bootstrapped and regtested on
> > x86_64-pc-linux-gnu and aarch64-unknown-linux-gnu, OK for trunk?
> >
> > Or maybe it would be clearer to do the #ifdef __cplusplus here locally
> > rather than introducing a new __GTHREAD_VAR macro?
>
> Actually, had a look again at this and I don't see what this is trying to
> fix.
> A function-local static requires __cxa_guard_acquire only if it needs
> dynamic initialization, which is not the case here (the initializers are
> = -1).  The non-dynamic ones are just initialized that way without any
> __cxa_guard_acquire.

Whoops, yes of course. Raising the topic of __cxa_guard_acquire was my
fault, and doesn't apply here, sorry.

But we still want to get rid of the STB_GNU_UNIQUE binding for the
static variables, don't we?

I've lost track a bit, so please correct me if I've got this wrong:

We originally had global static variables, which means a different
variable per TU. That causes ODR violations which were silently
ignored until we try to use them in modules, where they're diagnosed.
So we need to replace them.

Currently on trunk we have local statics, which avoid the ODR
violations, but emit STB_GNU_UNIQUE symbols, which are not zero-cost.
(There are no __cxa_guard_acquire issues, that was just my mistake.)

Jason suggested inline variables instead of local statics. That just
uses vague linkage for them (comdat), so no STB_GNU_UNIQUE, right?

Whether we choose local static or inline globals, these symbols will
now be visible across TUs and so become part of the ABI. Previously
they weren't, because every TU had its own copy which was manipulated
by always_inline static functions. That meant each TU had to
initialize its own variable, so we did the pthread_once trigger
multiple times (once per TU). But we could change the type of the
variable or the body of the always_inline static functions because
nothing affected other TUs. Whatever we do now, solving the ODR
violations for modules seems to require sharing variables between TUs
in one way or another, right?






>
> So, the function-local static seems more portable than hoping inline vars
> will be supported even for older C++ versions.
>
>         Jakub
>
Jakub Jelinek Oct. 8, 2024, 10:11 a.m. UTC | #3
On Tue, Oct 08, 2024 at 10:57:06AM +0100, Jonathan Wakely wrote:
> Whoops, yes of course. Raising the topic of __cxa_guard_acquire was my
> fault, and doesn't apply here, sorry.
> 
> But we still want to get rid of the STB_GNU_UNIQUE binding for the
> static variables, don't we?

I think it is the other way around.  At least when I try:
static inline volatile int *foo (void) { static volatile int v = -1; return &v; }
inline volatile int z = -1;
volatile int *p = foo ();

z is STB_GNU_UNIQUE, _ZZL3foovE1v is not.  Whether that is a bug or not, I
don't know...

	Jakub
Jonathan Wakely Oct. 8, 2024, 10:32 a.m. UTC | #4
On Tue, 8 Oct 2024 at 11:11, Jakub Jelinek <jakub@redhat.com> wrote:
>
> On Tue, Oct 08, 2024 at 10:57:06AM +0100, Jonathan Wakely wrote:
> > Whoops, yes of course. Raising the topic of __cxa_guard_acquire was my
> > fault, and doesn't apply here, sorry.
> >
> > But we still want to get rid of the STB_GNU_UNIQUE binding for the
> > static variables, don't we?
>
> I think it is the other way around.  At least when I try:
> static inline volatile int *foo (void) { static volatile int v = -1; return &v; }
> inline volatile int z = -1;
> volatile int *p = foo ();
>
> z is STB_GNU_UNIQUE, _ZZL3foovE1v is not.  Whether that is a bug or not, I
> don't know...

But that's because foo() is declared static, which we can't do because
of C++ modules. That was the original problem Nathaniel was fixing.

If you remove the 'static' from foo() then both z and _ZZ3foovE1v use
@gnu_unique_object
Jakub Jelinek Oct. 8, 2024, 10:34 a.m. UTC | #5
On Tue, Oct 08, 2024 at 11:32:16AM +0100, Jonathan Wakely wrote:
> But that's because foo() is declared static, which we can't do because
> of C++ modules. That was the original problem Nathaniel was fixing.
> 
> If you remove the 'static' from foo() then both z and _ZZ3foovE1v use
> @gnu_unique_object

Oops, pilot error.

	Jakub
Michael Matz Oct. 8, 2024, 12:58 p.m. UTC | #6
Hello,

On Tue, 8 Oct 2024, Jonathan Wakely wrote:

> We originally had global static variables, which means a different
> variable per TU. That causes ODR violations which were silently
> ignored until we try to use them in modules, where they're diagnosed.
> So we need to replace them.

Aren't these variables implementation detail?  If so violating language 
rules like ODR should be okay.  (Imagine libgcc would be written in some 
language where such rules doesn't exist).

So isn't the actual problem that needs solving rather that c++ modules 
trip over these here?


Ciao,
Michael.
Jonathan Wakely Oct. 8, 2024, 2:44 p.m. UTC | #7
On Tue, 8 Oct 2024 at 13:59, Michael Matz <matz@suse.de> wrote:
>
> Hello,
>
> On Tue, 8 Oct 2024, Jonathan Wakely wrote:
>
> > We originally had global static variables, which means a different
> > variable per TU. That causes ODR violations which were silently
> > ignored until we try to use them in modules, where they're diagnosed.
> > So we need to replace them.
>
> Aren't these variables implementation detail?  If so violating language
> rules like ODR should be okay.  (Imagine libgcc would be written in some
> language where such rules doesn't exist).
>
> So isn't the actual problem that needs solving rather that c++ modules
> trip over these here?

Yes, I was thinking the same thing. Maybe we need a new attribute that
tells the front end not to diagnose the ODR violations for these even
in C++ modules.
Jason Merrill Oct. 9, 2024, 10:55 p.m. UTC | #8
On 10/8/24 8:58 AM, Michael Matz wrote:
> Hello,
> 
> On Tue, 8 Oct 2024, Jonathan Wakely wrote:
> 
>> We originally had global static variables, which means a different
>> variable per TU. That causes ODR violations which were silently
>> ignored until we try to use them in modules, where they're diagnosed.
>> So we need to replace them.
> 
> Aren't these variables implementation detail?  If so violating language
> rules like ODR should be okay.  (Imagine libgcc would be written in some
> language where such rules doesn't exist).
> 
> So isn't the actual problem that needs solving rather that c++ modules
> trip over these here?

That's an interesting point; we need to handle internal variables in 
header units, I'm not sure how much trouble it would be to provide a way 
to suppress the exposure diagnostic for particular statics in a named 
module.

Jason
diff mbox series

Patch

diff --git a/libgcc/gthr-posix.h b/libgcc/gthr-posix.h
index 478bcf4cee6..b24992a539f 100644
--- a/libgcc/gthr-posix.h
+++ b/libgcc/gthr-posix.h
@@ -55,8 +55,10 @@  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 
 #ifdef __cplusplus
 # define __GTHREAD_INLINE inline __GTHREAD_ALWAYS_INLINE
+# define __GTHREAD_VAR inline
 #else
 # define __GTHREAD_INLINE static inline
+# define __GTHREAD_VAR static
 #endif
 
 typedef pthread_t __gthread_t;
@@ -198,18 +200,16 @@  __gthrw(pthread_setschedparam)
 #if defined(__FreeBSD__) || (defined(__sun) && defined(__svr4__))
 
 #pragma GCC visibility push(hidden)
-__GTHREAD_INLINE volatile int *
-__gthread_active (void)
-{
-  static volatile int __gthread_active_var = -1;
-  return &__gthread_active_var;
-}
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wc++17-extensions"
+__GTHREAD_VAR volatile int * __gthread_active = -1;
+#pragma GCC diagnostic pop
 #pragma GCC visibility pop
 
 __GTHREAD_INLINE void
 __gthread_trigger (void)
 {
-  *__gthread_active () = 1;
+  *__gthread_active = 1;
 }
 
 #pragma GCC visibility push(hidden)
@@ -220,7 +220,7 @@  __gthread_active_p (void)
   static pthread_once_t __gthread_active_once = PTHREAD_ONCE_INIT;
 
   /* Avoid reading __gthread_active twice on the main code path.  */
-  int __gthread_active_latest_value = *__gthread_active ();
+  int __gthread_active_latest_value = *__gthread_active;
 
   /* This test is not protected to avoid taking a lock on the main code
      path so every update of __gthread_active in a threaded program must
@@ -237,10 +237,10 @@  __gthread_active_p (void)
 	}
 
       /* Make sure we'll never enter this block again.  */
-      if (*__gthread_active () < 0)
-	*__gthread_active () = 0;
+      if (*__gthread_active < 0)
+	*__gthread_active = 0;
 
-      __gthread_active_latest_value = *__gthread_active ();
+      __gthread_active_latest_value = *__gthread_active;
     }
 
   return __gthread_active_latest_value != 0;
@@ -315,26 +315,24 @@  __gthread_active_p (void)
 #if defined(__hppa__) && defined(__hpux__)
 
 #pragma GCC visibility push(hidden)
-__GTHREAD_INLINE volatile int *
-__gthread_active (void)
-{
-  static volatile int __gthread_active_var = -1;
-  return &__gthread_active_var;
-}
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wc++17-extensions"
+__GTHREAD_VAR volatile int * __gthread_active = -1;
+#pragma GCC diagnostic pop
 #pragma GCC visibility pop
 
 __GTHREAD_INLINE int
 __gthread_active_p (void)
 {
   /* Avoid reading __gthread_active twice on the main code path.  */
-  int __gthread_active_latest_value = *__gthread_active ();
+  int __gthread_active_latest_value = *__gthread_active;
   size_t __s;
 
   if (__builtin_expect (__gthread_active_latest_value < 0, 0))
     {
       pthread_default_stacksize_np (0, &__s);
-      *__gthread_active () = __s ? 1 : 0;
-      __gthread_active_latest_value = *__gthread_active ();
+      *__gthread_active = __s ? 1 : 0;
+      __gthread_active_latest_value = *__gthread_active;
     }
 
   return __gthread_active_latest_value != 0;
@@ -980,6 +978,7 @@  __gthread_rwlock_unlock (__gthread_rwlock_t *__rwlock)
 
 #endif /* _LIBOBJC */
 
+#undef __GTHREAD_VAR
 #undef __GTHREAD_INLINE
 #undef __GTHREAD_ALWAYS_INLINE