diff mbox

[C++] Implement new "force_static" attribute

Message ID 1372693018.1789.110.camel@surprise
State New
Headers show

Commit Message

David Malcolm July 1, 2013, 3:36 p.m. UTC
My plan for removal of global variables in gcc 4.9 [1] calls for several
hundred new classes, which will be singletons in a classic monolithic
build, but have multiple instances in a shared-library build.

In order to avoid the register pressure of passing a redundant "this"
pointer around for the classic case, I've been looking at optimizing
singletons.

I'm attaching an optimization for this: a new "force_static" attribute
for the C++ frontend, which when added to a class implicitly adds
"static" to all members of said class.  This gives a way of avoiding a
"this" pointer in the classic build (in stages 2 and 3, once the
attribute is recognized), whilst supporting it in a shared-library
build, with relatively little boilerplate, preprocessor hackery or
syntactic differences.

See:
http://dmalcolm.fedorapeople.org/gcc/global-state/singletons.html#another-singleton-removal-optimization
for more information on how this would be used in GCC itself.

With this optimization, the generated machine code *with classes* (with
"methods" and "fields") is identical to that with just functions and
global variables (apart from the ordering of the functions/"methods"
within the .text sections of their respective .o files). [2]

FWIW I've also been looking at another approach:
http://dmalcolm.fedorapeople.org/gcc/global-state/singletons.html#a-singleton-removal-optimization
which is even lower boilerplate, though I don't have that working yet;
it touches the internals of classes and methods much more deeply.

BTW, I'm not 100% sold on "force_static" as the name of the attribute;
would "implicit_static" be a better name? (the latter is growing on me).

Successfully bootstrapped on x86_64-unknown-linux-gnu; all old testcases
have the same results as an unpatched build, and all new testcases pass
(using r200562 as the baseline).

Dave
[1] See http://gcc.gnu.org/ml/gcc/2013-06/msg00215.html
[2] I've written an "asmdiff" tool to help check this:
https://github.com/davidmalcolm/asmdiff

Comments

David Malcolm July 1, 2013, 6:02 p.m. UTC | #1
On Mon, 2013-07-01 at 11:36 -0400, David Malcolm wrote:

Sorry, I forgot the ChangeLog entries:

gcc/
2013-07-01  David Malcolm  <dmalcolm@redhat.com>

	* doc/extend.texi (Type Attributes): Document new "force_static"
	attribute.

gcc/cp/
2013-07-01  David Malcolm  <dmalcolm@redhat.com>

	* cp-tree.h (struct lang_type_class): Add force_static bitfield.
	(CLASSTYPE_FORCE_STATIC): New macro.
	* decl.c (grokfndecl): Silently discard "const" on methods
	within a "force_static" class.
	(grokdeclarator): Implement the effect of the "force_static"
	attribute.
	* ptree.c (cxx_print_type): Indicate when a class has been
	marked with "force_static".
	* tree.c (cxx_attribute_table): Add an entry for a new
	"force_static" attribute.
	(handle_force_static_attribute): New.

gcc/testsuite/
2013-07-01  David Malcolm  <dmalcolm@redhat.com>

	New "force_static" attribute.
	* g++.dg/ext/force_static/attribute.C: New.
	* g++.dg/ext/force_static/callsite.C: Likewise.
	* g++.dg/ext/force_static/const.C: Likewise.
	* g++.dg/ext/force_static/ctor.C: Likewise.
	* g++.dg/ext/force_static/dtor.C: Likewise.
	* g++.dg/ext/force_static/method.C: Likewise.
	* g++.dg/ext/force_static/not-a-class.C: Likewise.
	* g++.dg/ext/force_static/struct.C: Likewise.
	* g++.dg/ext/force_static/trailing.C: Likewise.
	* g++.dg/ext/force_static/vfunc.C: Likewise.
Gabriel Dos Reis July 2, 2013, 1:41 a.m. UTC | #2
On Mon, Jul 1, 2013 at 10:36 AM, David Malcolm <dmalcolm@redhat.com> wrote:
> My plan for removal of global variables in gcc 4.9 [1] calls for several
> hundred new classes, which will be singletons in a classic monolithic
> build, but have multiple instances in a shared-library build.
>
> In order to avoid the register pressure of passing a redundant "this"
> pointer around for the classic case, I've been looking at optimizing
> singletons.
>
> I'm attaching an optimization for this: a new "force_static" attribute
> for the C++ frontend, which when added to a class implicitly adds
> "static" to all members of said class.  This gives a way of avoiding a
> "this" pointer in the classic build (in stages 2 and 3, once the
> attribute is recognized), whilst supporting it in a shared-library
> build, with relatively little boilerplate, preprocessor hackery or
> syntactic differences.
>
> See:
> http://dmalcolm.fedorapeople.org/gcc/global-state/singletons.html#another-singleton-removal-optimization
> for more information on how this would be used in GCC itself.

Hi David,

I am still a little bit confused by this.  Help me out:
   1. if we don't need to pass `this', why should we ever find
       ourselves to writing functions that need one in the first place?
       How do shared libraries get into this water?

   2. regarding your comment about constexpr.  If you think that is useful
       you can add it to gnu++03 as __constexpr__ and use it, following
       the pattern suggested by RTH.

   3.  Is it that GCC does not know how to optimize objects of empty classes?


>
> With this optimization, the generated machine code *with classes* (with
> "methods" and "fields") is identical to that with just functions and
> global variables (apart from the ordering of the functions/"methods"
> within the .text sections of their respective .o files). [2]
>
> FWIW I've also been looking at another approach:
> http://dmalcolm.fedorapeople.org/gcc/global-state/singletons.html#a-singleton-removal-optimization
> which is even lower boilerplate, though I don't have that working yet;
> it touches the internals of classes and methods much more deeply.
>
> BTW, I'm not 100% sold on "force_static" as the name of the attribute;
> would "implicit_static" be a better name? (the latter is growing on me).
>
> Successfully bootstrapped on x86_64-unknown-linux-gnu; all old testcases
> have the same results as an unpatched build, and all new testcases pass
> (using r200562 as the baseline).
>
> Dave
> [1] See http://gcc.gnu.org/ml/gcc/2013-06/msg00215.html
> [2] I've written an "asmdiff" tool to help check this:
> https://github.com/davidmalcolm/asmdiff
Marc Glisse July 2, 2013, 6:16 a.m. UTC | #3
On Mon, 1 Jul 2013, Gabriel Dos Reis wrote:

> On Mon, Jul 1, 2013 at 10:36 AM, David Malcolm <dmalcolm@redhat.com> wrote:
>> My plan for removal of global variables in gcc 4.9 [1] calls for several
>> hundred new classes, which will be singletons in a classic monolithic
>> build, but have multiple instances in a shared-library build.
>>
>> In order to avoid the register pressure of passing a redundant "this"
>> pointer around for the classic case, I've been looking at optimizing
>> singletons.
>>
>> I'm attaching an optimization for this: a new "force_static" attribute
>> for the C++ frontend, which when added to a class implicitly adds
>> "static" to all members of said class.  This gives a way of avoiding a
>> "this" pointer in the classic build (in stages 2 and 3, once the
>> attribute is recognized), whilst supporting it in a shared-library
>> build, with relatively little boilerplate, preprocessor hackery or
>> syntactic differences.
>>
>> See:
>> http://dmalcolm.fedorapeople.org/gcc/global-state/singletons.html#another-singleton-removal-optimization
>> for more information on how this would be used in GCC itself.
>
> Hi David,
>
> I am still a little bit confused by this.  Help me out:
>   1. if we don't need to pass `this', why should we ever find
>       ourselves to writing functions that need one in the first place?
>       How do shared libraries get into this water?

In theory, there is always this regular class with data and member 
functions that use *this. However, for traditional use (not in a library), 
there will be a single global instance of this class. For optimization 
purposes, it seems better in that case to make all members (variables and 
functions) static to let the compiler use a constant address instead of 
passing "this" around.

I don't know exactly how hard it would be for the compiler to do this 
optimization without the force_static hint, but it would need to detect 
that there is only ever one object created (using -fwhole-program? 
visibility?). Or to clone the member functions for each global instance if 
it is always clear on which one the function is called (but how do you 
determine it is worth it?).

>   3.  Is it that GCC does not know how to optimize objects of empty classes?

GCC indeed isn't very good at that (partially because of the ABI), but I 
don't think that's the point here.

>> With this optimization, the generated machine code *with classes* (with
>> "methods" and "fields") is identical to that with just functions and
>> global variables (apart from the ordering of the functions/"methods"
>> within the .text sections of their respective .o files). [2]
>>
>> FWIW I've also been looking at another approach:
>> http://dmalcolm.fedorapeople.org/gcc/global-state/singletons.html#a-singleton-removal-optimization
>> which is even lower boilerplate, though I don't have that working yet;
>> it touches the internals of classes and methods much more deeply.
>>
>> BTW, I'm not 100% sold on "force_static" as the name of the attribute;
>> would "implicit_static" be a better name? (the latter is growing on me).
>>
>> Successfully bootstrapped on x86_64-unknown-linux-gnu; all old testcases
>> have the same results as an unpatched build, and all new testcases pass
>> (using r200562 as the baseline).
>>
>> Dave
>> [1] See http://gcc.gnu.org/ml/gcc/2013-06/msg00215.html
>> [2] I've written an "asmdiff" tool to help check this:
>> https://github.com/davidmalcolm/asmdiff
Gabriel Dos Reis July 2, 2013, 11:37 a.m. UTC | #4
On Tue, Jul 2, 2013 at 1:16 AM, Marc Glisse <marc.glisse@inria.fr> wrote:
> On Mon, 1 Jul 2013, Gabriel Dos Reis wrote:
>
>> On Mon, Jul 1, 2013 at 10:36 AM, David Malcolm <dmalcolm@redhat.com>
>> wrote:
>>>
>>> My plan for removal of global variables in gcc 4.9 [1] calls for several
>>> hundred new classes, which will be singletons in a classic monolithic
>>> build, but have multiple instances in a shared-library build.
>>>
>>> In order to avoid the register pressure of passing a redundant "this"
>>> pointer around for the classic case, I've been looking at optimizing
>>> singletons.
>>>
>>> I'm attaching an optimization for this: a new "force_static" attribute
>>> for the C++ frontend, which when added to a class implicitly adds
>>> "static" to all members of said class.  This gives a way of avoiding a
>>> "this" pointer in the classic build (in stages 2 and 3, once the
>>> attribute is recognized), whilst supporting it in a shared-library
>>> build, with relatively little boilerplate, preprocessor hackery or
>>> syntactic differences.
>>>
>>> See:
>>>
>>> http://dmalcolm.fedorapeople.org/gcc/global-state/singletons.html#another-singleton-removal-optimization
>>> for more information on how this would be used in GCC itself.
>>
>>
>> Hi David,
>>
>> I am still a little bit confused by this.  Help me out:
>>   1. if we don't need to pass `this', why should we ever find
>>       ourselves to writing functions that need one in the first place?
>>       How do shared libraries get into this water?
>
>
> In theory, there is always this regular class with data and member functions
> that use *this. However, for traditional use (not in a library), there will
> be a single global instance of this class. For optimization purposes, it
> seems better in that case to make all members (variables and functions)
> static to let the compiler use a constant address instead of passing "this"
> around.

Thanks, Marc!

From the description, I have the impression you are saying that the class
is essentially a singleton class.  Is that right?

Sorry, I am still confused about what the problem is and why we need this
solution -- I read several times David's links but I can't get my head
around the fundamental problem.

>
> I don't know exactly how hard it would be for the compiler to do this
> optimization without the force_static hint, but it would need to detect that
> there is only ever one object created (using -fwhole-program? visibility?).
> Or to clone the member functions for each global instance if it is always
> clear on which one the function is called (but how do you determine it is
> worth it?).
>
>
>>   3.  Is it that GCC does not know how to optimize objects of empty
>> classes?
>
>
> GCC indeed isn't very good at that (partially because of the ABI), but I
> don't think that's the point here.
>
>
>>> With this optimization, the generated machine code *with classes* (with
>>> "methods" and "fields") is identical to that with just functions and
>>> global variables (apart from the ordering of the functions/"methods"
>>> within the .text sections of their respective .o files). [2]
>>>
>>> FWIW I've also been looking at another approach:
>>>
>>> http://dmalcolm.fedorapeople.org/gcc/global-state/singletons.html#a-singleton-removal-optimization
>>> which is even lower boilerplate, though I don't have that working yet;
>>> it touches the internals of classes and methods much more deeply.
>>>
>>> BTW, I'm not 100% sold on "force_static" as the name of the attribute;
>>> would "implicit_static" be a better name? (the latter is growing on me).
>>>
>>> Successfully bootstrapped on x86_64-unknown-linux-gnu; all old testcases
>>> have the same results as an unpatched build, and all new testcases pass
>>> (using r200562 as the baseline).
>>>
>>> Dave
>>> [1] See http://gcc.gnu.org/ml/gcc/2013-06/msg00215.html
>>> [2] I've written an "asmdiff" tool to help check this:
>>> https://github.com/davidmalcolm/asmdiff
>
>
> --
> Marc Glisse
David Malcolm July 2, 2013, 5:01 p.m. UTC | #5
On Tue, 2013-07-02 at 06:37 -0500, Gabriel Dos Reis wrote:
> On Tue, Jul 2, 2013 at 1:16 AM, Marc Glisse <marc.glisse@inria.fr> wrote:
> > On Mon, 1 Jul 2013, Gabriel Dos Reis wrote:
> >
> >> On Mon, Jul 1, 2013 at 10:36 AM, David Malcolm <dmalcolm@redhat.com>
> >> wrote:
> >>>
> >>> My plan for removal of global variables in gcc 4.9 [1] calls for several
> >>> hundred new classes, which will be singletons in a classic monolithic
> >>> build, but have multiple instances in a shared-library build.
> >>>
> >>> In order to avoid the register pressure of passing a redundant "this"
> >>> pointer around for the classic case, I've been looking at optimizing
> >>> singletons.
> >>>
> >>> I'm attaching an optimization for this: a new "force_static" attribute
> >>> for the C++ frontend, which when added to a class implicitly adds
> >>> "static" to all members of said class.  This gives a way of avoiding a
> >>> "this" pointer in the classic build (in stages 2 and 3, once the
> >>> attribute is recognized), whilst supporting it in a shared-library
> >>> build, with relatively little boilerplate, preprocessor hackery or
> >>> syntactic differences.
> >>>
> >>> See:
> >>>
> >>> http://dmalcolm.fedorapeople.org/gcc/global-state/singletons.html#another-singleton-removal-optimization
> >>> for more information on how this would be used in GCC itself.
> >>
> >>
> >> Hi David,
> >>
> >> I am still a little bit confused by this.  Help me out:
> >>   1. if we don't need to pass `this', why should we ever find
> >>       ourselves to writing functions that need one in the first place?
> >>       How do shared libraries get into this water?
> >
> >
> > In theory, there is always this regular class with data and member functions
> > that use *this. However, for traditional use (not in a library), there will
> > be a single global instance of this class. For optimization purposes, it
> > seems better in that case to make all members (variables and functions)
> > static to let the compiler use a constant address instead of passing "this"
> > around.
> 
> Thanks, Marc!
> 
> From the description, I have the impression you are saying that the class
> is essentially a singleton class.  Is that right?
> 
> Sorry, I am still confused about what the problem is and why we need this
> solution -- I read several times David's links but I can't get my head
> around the fundamental problem.

Sorry about that.

I want to rework the internals of GCC so that they become thread-safe,
embeddable as a library inside another program.  

For example, a web browser may want to compile JavaScript to machine
code: each tab within the browser could be running on a separate thread
within one process, and each may want to create "compilation contexts"
that turn javascript into gimple, say, create a dedicated optimization
pipeline (perhaps with some custom passes), and send the gimple+pipeline
to the GCC-as-a-shared-library to get machine code back.  [well,
assembler, but binutils-as-a-library is currently out-of-scope for my
plan].

Given that this could all be happening on different threads, we could
just have one big mutex to avoid interference, but the better approach
is to isolate all of the state inside GCC so each compilation context
gets its own world of state.  Ideally there would be no global variables
internally in gcc, but currently there are about 3500 of them.

I want to provide the above, but I don't want to slow down the current
use-case of GCC: the family of monolithic binaries.  For example, simply
building as a shared library slows things down, since you'd have have to
build gcc as position-independent code, which itself incurs a slowdown.
So I envisage a "--enable-shared" configuration switch to opt-in to the
shared library code, but I want as minimize the difference between the
two cases.

The natural way to eliminate the various globals is to group
logically-related global variables into classes, together with the
functions that operate on them, as methods.  For example, the
callgraph-related code could be placed into a new "class callgraph", the
pass-management code into a new "class pipeline", the garbage-collector
internals into a new "class gc_heap", etc.  The various passes with
state typically get their own classes.

I estimate about 300 such classes.

Each client of gcc (e.g. each tab in a web browser) would have its own
context pointer into the GCC shared library, a handle to one "parallel
universe" of state, independent of all other such parallel universes.

Much of GCC's existing functions would become methods of one of the
above classes.  For example, most of the functions in cgraph.c,
cgraphbuild.c, cgraphclones.c and cgraphunit.c would become methods of a
"class callgraph" (much of these methods would be private); each
parallel universe of state would have its own callgraph instance.  The
graph of internal functions implementing the callgraph code become a
graph of *methods*, passing around a "this" pointer.

This should give us a relatively smooth transition path from the current
code to such a parallel-universe world, but there is a performance
concern: thousands of callsites throughout the compiler would gain a
"this" parameter that gets silently passed around, and dereferenced
throughout the code when reading/writing data which would have been a
global in an old version of gcc.

We'd have hundreds of these new classes which would be regular classes
when configured --with-shared, but these classes are all singletons when
configured --without-shared: the "this" pointer is redundant in this
latter configuration (as is the case for a conventional build of gcc, as
binaries).

The purpose of the attribute is to provide a way to write code as
regular classes, but have a relatively simple way of eliminating all of
the "this" uses in the --without-shared build, thus trivially making the
performance characteristics of the new gcc with --without-shared be the
same as the old gcc.

Hence we get relatively clean code (IMHO), the ability to have multiple
independent compilation contexts in a thread-safe gcc shared library,
and the same code shared with a non-shared-library gcc that has the same
performance characteristics as an existing version of gcc.

Hope the above makes more sense
Dave
David Malcolm July 2, 2013, 6:04 p.m. UTC | #6
On Tue, 2013-07-02 at 08:16 +0200, Marc Glisse wrote:
> On Mon, 1 Jul 2013, Gabriel Dos Reis wrote:
> 
> > On Mon, Jul 1, 2013 at 10:36 AM, David Malcolm <dmalcolm@redhat.com> wrote:
> >> My plan for removal of global variables in gcc 4.9 [1] calls for several
> >> hundred new classes, which will be singletons in a classic monolithic
> >> build, but have multiple instances in a shared-library build.
> >>
> >> In order to avoid the register pressure of passing a redundant "this"
> >> pointer around for the classic case, I've been looking at optimizing
> >> singletons.
> >>
> >> I'm attaching an optimization for this: a new "force_static" attribute
> >> for the C++ frontend, which when added to a class implicitly adds
> >> "static" to all members of said class.  This gives a way of avoiding a
> >> "this" pointer in the classic build (in stages 2 and 3, once the
> >> attribute is recognized), whilst supporting it in a shared-library
> >> build, with relatively little boilerplate, preprocessor hackery or
> >> syntactic differences.
> >>
> >> See:
> >> http://dmalcolm.fedorapeople.org/gcc/global-state/singletons.html#another-singleton-removal-optimization
> >> for more information on how this would be used in GCC itself.
> >
> > Hi David,
> >
> > I am still a little bit confused by this.  Help me out:
> >   1. if we don't need to pass `this', why should we ever find
> >       ourselves to writing functions that need one in the first place?
> >       How do shared libraries get into this water?
> 
> In theory, there is always this regular class with data and member 
> functions that use *this. However, for traditional use (not in a library), 
> there will be a single global instance of this class. For optimization 
> purposes, it seems better in that case to make all members (variables and 
> functions) static to let the compiler use a constant address instead of 
> passing "this" around.

Exactly (assuming that by "constant address" you're referring to the
address of each member variable, when it becomes static).

> I don't know exactly how hard it would be for the compiler to do this 
> optimization without the force_static hint, but it would need to detect 
> that there is only ever one object created (using -fwhole-program? 
> visibility?). Or to clone the member functions for each global instance if 
> it is always clear on which one the function is called (but how do you 
> determine it is worth it?).

It may work when the class is confined to a single TU, but in the
general case such an approach would also require building the compiler
with LTO to get back to the old performance, which might be a
significant imposition (e.g. needing plenty of RAM, for starters).

> 
> >   3.  Is it that GCC does not know how to optimize objects of empty classes?
> 
> GCC indeed isn't very good at that (partially because of the ABI), but I 
> don't think that's the point here.

Right, the objects would only be empty when marked with "force_static".

[...]
Joseph Myers July 2, 2013, 8:49 p.m. UTC | #7
On Tue, 2 Jul 2013, David Malcolm wrote:

> So I envisage a "--enable-shared" configuration switch to opt-in to the
> shared library code, but I want as minimize the difference between the
> two cases.

--enable-host-shared or --enable-shared-gcc or some other such option 
name, please, not plain --enable-shared with currently refers to the 
libraries built for the target.  Whether the target libraries should be 
shared libraries is a choice completely independent of whether to build 
GCC itself as a shared library (or libraries? - since you have lots of 
separate front ends that don't currently expect to be linked together, 
although actually making it possible to link them together is probably 
only a few days' work).
Andrew Pinski July 2, 2013, 8:54 p.m. UTC | #8
On Mon, Jul 1, 2013 at 8:36 AM, David Malcolm <dmalcolm@redhat.com> wrote:
> My plan for removal of global variables in gcc 4.9 [1] calls for several
> hundred new classes, which will be singletons in a classic monolithic
> build, but have multiple instances in a shared-library build.
>
> In order to avoid the register pressure of passing a redundant "this"
> pointer around for the classic case, I've been looking at optimizing
> singletons.
>
> I'm attaching an optimization for this: a new "force_static" attribute
> for the C++ frontend, which when added to a class implicitly adds
> "static" to all members of said class.  This gives a way of avoiding a
> "this" pointer in the classic build (in stages 2 and 3, once the
> attribute is recognized), whilst supporting it in a shared-library
> build, with relatively little boilerplate, preprocessor hackery or
> syntactic differences.
>
> See:
> http://dmalcolm.fedorapeople.org/gcc/global-state/singletons.html#another-singleton-removal-optimization
> for more information on how this would be used in GCC itself.
>
> With this optimization, the generated machine code *with classes* (with
> "methods" and "fields") is identical to that with just functions and
> global variables (apart from the ordering of the functions/"methods"
> within the .text sections of their respective .o files). [2]
>
> FWIW I've also been looking at another approach:
> http://dmalcolm.fedorapeople.org/gcc/global-state/singletons.html#a-singleton-removal-optimization
> which is even lower boilerplate, though I don't have that working yet;
> it touches the internals of classes and methods much more deeply.
>
> BTW, I'm not 100% sold on "force_static" as the name of the attribute;
> would "implicit_static" be a better name? (the latter is growing on me).
>
> Successfully bootstrapped on x86_64-unknown-linux-gnu; all old testcases
> have the same results as an unpatched build, and all new testcases pass
> (using r200562 as the baseline).

I am not a big fan of adding another extension to GCC.  Especially one
where the documentation does not describe all the interactions with
templates or all of the C++ features.  Also Why can't we use some
preprocess tricks instead of adding this extension?

Thanks,
Andrew Pinski


>
> Dave
> [1] See http://gcc.gnu.org/ml/gcc/2013-06/msg00215.html
> [2] I've written an "asmdiff" tool to help check this:
> https://github.com/davidmalcolm/asmdiff
David Malcolm July 2, 2013, 9:09 p.m. UTC | #9
On Tue, 2013-07-02 at 13:54 -0700, Andrew Pinski wrote:
> On Mon, Jul 1, 2013 at 8:36 AM, David Malcolm <dmalcolm@redhat.com> wrote:
> > My plan for removal of global variables in gcc 4.9 [1] calls for several
> > hundred new classes, which will be singletons in a classic monolithic
> > build, but have multiple instances in a shared-library build.
> >
> > In order to avoid the register pressure of passing a redundant "this"
> > pointer around for the classic case, I've been looking at optimizing
> > singletons.
> >
> > I'm attaching an optimization for this: a new "force_static" attribute
> > for the C++ frontend, which when added to a class implicitly adds
> > "static" to all members of said class.  This gives a way of avoiding a
> > "this" pointer in the classic build (in stages 2 and 3, once the
> > attribute is recognized), whilst supporting it in a shared-library
> > build, with relatively little boilerplate, preprocessor hackery or
> > syntactic differences.
> >
> > See:
> > http://dmalcolm.fedorapeople.org/gcc/global-state/singletons.html#another-singleton-removal-optimization
> > for more information on how this would be used in GCC itself.
> >
> > With this optimization, the generated machine code *with classes* (with
> > "methods" and "fields") is identical to that with just functions and
> > global variables (apart from the ordering of the functions/"methods"
> > within the .text sections of their respective .o files). [2]
> >
> > FWIW I've also been looking at another approach:
> > http://dmalcolm.fedorapeople.org/gcc/global-state/singletons.html#a-singleton-removal-optimization
> > which is even lower boilerplate, though I don't have that working yet;
> > it touches the internals of classes and methods much more deeply.
> >
> > BTW, I'm not 100% sold on "force_static" as the name of the attribute;
> > would "implicit_static" be a better name? (the latter is growing on me).
> >
> > Successfully bootstrapped on x86_64-unknown-linux-gnu; all old testcases
> > have the same results as an unpatched build, and all new testcases pass
> > (using r200562 as the baseline).
> 
> I am not a big fan of adding another extension to GCC.  Especially one
> where the documentation does not describe all the interactions with
> templates or all of the C++ features.

Right; it doesn't support templates yet, and reviewing my own patch it
looks like the interaction with ctors and dtors could use some work, at
least.  (sorry)

I think a renaming to "implicit_static" may help, so I'll do that in the
next iteration.

Are there other aspects that would need documentation/improvement for
you to be happier with this?

Out of interest, how do you feel about the alternate "singleton"
attribute proposed in my plan:
http://dmalcolm.fedorapeople.org/gcc/global-state/singletons.html#a-singleton-removal-optimization
(I have only a partially-working implementation of this other
attribute).

>   Also Why can't we use some
> preprocess tricks instead of adding this extension?

FWIW I initially did go down the preprocessor route: see
http://dmalcolm.fedorapeople.org/gcc/global-state/singletons.html#other-ways-to-optimize-singletons
but doing so involves a *lot* of macros: every member function and
variable needs to be marked with "MAYBE_STATIC".

The advantage of the attribute is that the macro markings can be done
per-class rather than per-member, for (waving hands) an
order-of-magnitude fewer macro uses.

Thanks
Dave
David Malcolm July 2, 2013, 9:33 p.m. UTC | #10
On Tue, 2013-07-02 at 20:49 +0000, Joseph S. Myers wrote:
> On Tue, 2 Jul 2013, David Malcolm wrote:
> 
> > So I envisage a "--enable-shared" configuration switch to opt-in to the
> > shared library code, but I want as minimize the difference between the
> > two cases.
> 
> --enable-host-shared or --enable-shared-gcc or some other such option 
> name, please, not plain --enable-shared with currently refers to the 
> libraries built for the target.  Whether the target libraries should be 
> shared libraries is a choice completely independent of whether to build 
> GCC itself as a shared library

Good catch; thanks.  I've added a note on this to my plan doc. [1]

>  (or libraries? - since you have lots of 
> separate front ends that don't currently expect to be linked together, 
> although actually making it possible to link them together is probably 
> only a few days' work).

I think a lot of this will be dependent on Andrew's big frontend vs
backend cleanup, but yes, multiple libraries would be much nicer.

Thanks
Dave

[1]
https://github.com/davidmalcolm/gcc-global-state/commit/a5bf16116a6da217981a9c94f8f999fec995dfc7
Andrew Pinski July 2, 2013, 10:23 p.m. UTC | #11
On Tue, Jul 2, 2013 at 2:09 PM, David Malcolm <dmalcolm@redhat.com> wrote:
> On Tue, 2013-07-02 at 13:54 -0700, Andrew Pinski wrote:
>> On Mon, Jul 1, 2013 at 8:36 AM, David Malcolm <dmalcolm@redhat.com> wrote:
>> > My plan for removal of global variables in gcc 4.9 [1] calls for several
>> > hundred new classes, which will be singletons in a classic monolithic
>> > build, but have multiple instances in a shared-library build.
>> >
>> > In order to avoid the register pressure of passing a redundant "this"
>> > pointer around for the classic case, I've been looking at optimizing
>> > singletons.
>> >
>> > I'm attaching an optimization for this: a new "force_static" attribute
>> > for the C++ frontend, which when added to a class implicitly adds
>> > "static" to all members of said class.  This gives a way of avoiding a
>> > "this" pointer in the classic build (in stages 2 and 3, once the
>> > attribute is recognized), whilst supporting it in a shared-library
>> > build, with relatively little boilerplate, preprocessor hackery or
>> > syntactic differences.
>> >
>> > See:
>> > http://dmalcolm.fedorapeople.org/gcc/global-state/singletons.html#another-singleton-removal-optimization
>> > for more information on how this would be used in GCC itself.
>> >
>> > With this optimization, the generated machine code *with classes* (with
>> > "methods" and "fields") is identical to that with just functions and
>> > global variables (apart from the ordering of the functions/"methods"
>> > within the .text sections of their respective .o files). [2]
>> >
>> > FWIW I've also been looking at another approach:
>> > http://dmalcolm.fedorapeople.org/gcc/global-state/singletons.html#a-singleton-removal-optimization
>> > which is even lower boilerplate, though I don't have that working yet;
>> > it touches the internals of classes and methods much more deeply.
>> >
>> > BTW, I'm not 100% sold on "force_static" as the name of the attribute;
>> > would "implicit_static" be a better name? (the latter is growing on me).
>> >
>> > Successfully bootstrapped on x86_64-unknown-linux-gnu; all old testcases
>> > have the same results as an unpatched build, and all new testcases pass
>> > (using r200562 as the baseline).
>>
>> I am not a big fan of adding another extension to GCC.  Especially one
>> where the documentation does not describe all the interactions with
>> templates or all of the C++ features.
>
> Right; it doesn't support templates yet, and reviewing my own patch it
> looks like the interaction with ctors and dtors could use some work, at
> least.  (sorry)
>
> I think a renaming to "implicit_static" may help, so I'll do that in the
> next iteration.
>
> Are there other aspects that would need documentation/improvement for
> you to be happier with this?
>
> Out of interest, how do you feel about the alternate "singleton"
> attribute proposed in my plan:
> http://dmalcolm.fedorapeople.org/gcc/global-state/singletons.html#a-singleton-removal-optimization
> (I have only a partially-working implementation of this other
> attribute).
>
>>   Also Why can't we use some
>> preprocess tricks instead of adding this extension?
>
> FWIW I initially did go down the preprocessor route: see
> http://dmalcolm.fedorapeople.org/gcc/global-state/singletons.html#other-ways-to-optimize-singletons
> but doing so involves a *lot* of macros: every member function and
> variable needs to be marked with "MAYBE_STATIC".
>
> The advantage of the attribute is that the macro markings can be done
> per-class rather than per-member, for (waving hands) an
> order-of-magnitude fewer macro uses.


I think the per-class method is just going to get messy in the sense
you have to go back and find the attribute.  In the per-member it is
easier to find and easier to see.  If you want to go this route, I
think you should go and propose this to the C++ standard committee
first :).

Thanks,
Andrew

>
> Thanks
> Dave
>
diff mbox

Patch

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 2931ac5..17cf35e 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -1416,6 +1416,7 @@  struct GTY(()) lang_type_class {
   unsigned has_complex_move_assign : 1;
   unsigned has_constexpr_ctor : 1;
   unsigned is_final : 1;
+  unsigned force_static : 1;
 
   /* When adding a flag here, consider whether or not it ought to
      apply to a template instance if it applies to the template.  If
@@ -1424,7 +1425,7 @@  struct GTY(()) lang_type_class {
   /* There are some bits left to fill out a 32-bit word.  Keep track
      of this by updating the size of this bitfield whenever you add or
      remove a flag.  */
-  unsigned dummy : 2;
+  unsigned dummy : 1;
 
   tree primary_base;
   vec<tree_pair_s, va_gc> *vcall_indices;
@@ -1536,6 +1537,10 @@  struct GTY((variable_size)) lang_type {
 #define CLASSTYPE_FINAL(NODE) \
   (LANG_TYPE_CLASS_CHECK (NODE)->is_final)
 
+/* Nonzero means that NODE (a class type) has the force_static
+   attribute.  */
+#define CLASSTYPE_FORCE_STATIC(NODE) \
+  (LANG_TYPE_CLASS_CHECK (NODE)->force_static)
 
 /* Nonzero means that this _CLASSTYPE node overloads operator=(X&).  */
 #define TYPE_HAS_COPY_ASSIGN(NODE) (LANG_TYPE_CLASS_CHECK (NODE)->has_copy_assign)
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 9eb1d12..d12b2a8 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -7575,10 +7575,24 @@  grokfndecl (tree ctype,
     {
       if (quals)
 	{
-	  error (ctype
-		 ? G_("static member function %qD cannot have cv-qualifier")
-		 : G_("non-member function %qD cannot have cv-qualifier"),
-		 decl);
+	  if (ctype)
+	    {
+	      if (CLASS_TYPE_P (ctype) && CLASSTYPE_FORCE_STATIC (ctype))
+		/* It's useful to be able to mark methods of force_static
+		   classes as "const" etc so that we can toggle the
+		   presence of the "force_static" attribute without needing
+		   to remove the const qualifiers.
+
+		   Silently discard such markings.  */
+		;
+	      else
+		error (G_("static member function %qD cannot have"
+			  " cv-qualifier"),
+		       decl);
+	    }
+	  else
+	    error (G_("non-member function %qD cannot have cv-qualifier"),
+		   decl);
 	  quals = TYPE_UNQUALIFIED;
 	}
 
@@ -9243,7 +9257,9 @@  grokdeclarator (const cp_declarator *declarator,
   explicitp = decl_spec_seq_has_spec_p (declspecs, ds_explicit);
 
   storage_class = declspecs->storage_class;
-  if (storage_class == sc_static)
+  if (storage_class == sc_static
+      || (current_class_type &&
+	  CLASSTYPE_FORCE_STATIC (current_class_type)))
     staticp = 1 + (decl_context == FIELD);
 
   if (virtualp && staticp == 2)
diff --git a/gcc/cp/ptree.c b/gcc/cp/ptree.c
index f4ca003..f4e5cd0 100644
--- a/gcc/cp/ptree.c
+++ b/gcc/cp/ptree.c
@@ -165,6 +165,8 @@  cxx_print_type (FILE *file, tree node, int indent)
 	fprintf (file, " interface-only");
       if (CLASSTYPE_INTERFACE_UNKNOWN (node))
 	fprintf (file, " interface-unknown");
+      if (CLASSTYPE_FORCE_STATIC (node))
+	fprintf (file, " force_static");
     }
 }
 
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 8524f6c..b5d5879 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -47,6 +47,7 @@  static tree handle_java_interface_attribute (tree *, tree, tree, int, bool *);
 static tree handle_com_interface_attribute (tree *, tree, tree, int, bool *);
 static tree handle_init_priority_attribute (tree *, tree, tree, int, bool *);
 static tree handle_abi_tag_attribute (tree *, tree, tree, int, bool *);
+static tree handle_force_static_attribute (tree *, tree, tree, int, bool *);
 
 /* If REF is an lvalue, returns the kind of lvalue that REF is.
    Otherwise, returns clk_none.  */
@@ -3157,6 +3158,8 @@  const struct attribute_spec cxx_attribute_table[] =
     handle_init_priority_attribute, false },
   { "abi_tag", 1, -1, false, false, false,
     handle_abi_tag_attribute, true },
+  { "force_static", 0, 0, false, true, false,
+    handle_force_static_attribute, false },
   { NULL,	      0, 0, false, false, false, NULL, false }
 };
 
@@ -3378,6 +3381,39 @@  handle_abi_tag_attribute (tree* node, tree name, tree args,
   return NULL_TREE;
 }
 
+/* Handle a "force_static" attribute; arguments as in
+   struct attribute_spec.handler.  */
+static tree
+handle_force_static_attribute (tree* node,
+			       tree name,
+			       tree /*args*/,
+			       int /*flags*/,
+			       bool* /*no_add_attrs*/)
+{
+  if (!CLASS_TYPE_P (*node))
+    {
+      warning (OPT_Wattributes, "%qE attribute can only be applied "
+	       "to class definitions", name);
+      return NULL_TREE;
+    }
+
+ /* The attribute affects how decls within the class body are grokked.
+    If the attribute is seen after the body of the class has been handled,
+    it's too late.  */
+  if (TYPE_FIELDS (*node))
+    {
+      warning (OPT_Wattributes,
+	       ("%qE attribute must be applied after the class keyword,"
+		" not after the closing brace"),
+	       name);
+      return NULL_TREE;
+    }
+
+  CLASSTYPE_FORCE_STATIC (*node) = 1;
+
+  return NULL_TREE;
+}
+
 /* Return a new PTRMEM_CST of the indicated TYPE.  The MEMBER is the
    thing pointed to by the constant.  */
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 6ce26ef..6d33191 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5539,6 +5539,46 @@  and caught in another, the class must have default visibility.
 Otherwise the two shared objects are unable to use the same
 typeinfo node and exception handling will break.
 
+@item force_static
+In C++, a class or struct can be marked with the ``force_static'' attribute
+to make it easier to implement singletons.
+
+This implicitly adds the ``static'' keyword to all methods and data, so
+that the methods lose the implicit ``this'' that they would otherwise
+have, thus allowing for more efficient generated code.
+
+The attribute must appear between the ``class'' or ``struct'' keyword and
+the name of the type; it may not appear between the closing parenthesis
+of the body of the type and the trailing semicolon.
+
+@smallexample
+class  __attribute__((force_static)) foo
+@{
+public:
+  void some_method ();
+
+private:
+  int some_field;
+@};
+
+/* Instances of the type can still be created, but they become empty.  */
+extern foo the_foo;
+
+/* Any instances share the same data, as if there was just one
+   instance.  */
+int foo::some_field;
+
+void bar (void)
+@{
+  /* You can invoke methods on such a class...  */
+  the_foo.some_method ();
+
+  /* ...where the attribute makes the above be implicitly equivalent to
+     this:  */
+  foo::some_method ();
+@}
+@end smallexample
+
 @end table
 
 To specify multiple attributes, separate them by commas within the
diff --git a/gcc/testsuite/g++.dg/ext/force_static/attribute.C b/gcc/testsuite/g++.dg/ext/force_static/attribute.C
new file mode 100644
index 0000000..5f315e4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/force_static/attribute.C
@@ -0,0 +1,7 @@ 
+class  __attribute__((force_static)) foo
+{
+private:
+  int i;
+};
+
+int foo::i;
diff --git a/gcc/testsuite/g++.dg/ext/force_static/callsite.C b/gcc/testsuite/g++.dg/ext/force_static/callsite.C
new file mode 100644
index 0000000..3a11884
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/force_static/callsite.C
@@ -0,0 +1,19 @@ 
+// { dg-options "-fdump-tree-original" }
+
+class  __attribute__((force_static)) foo
+{
+public:
+  int some_method (int i);
+
+private:
+  int some_field;
+};
+
+int test_callsite ()
+{
+  extern foo the_foo;
+  return the_foo.some_method (42);
+}
+
+// { dg-final { scan-tree-dump "foo::some_method" "original" } }
+// { dg-final { cleanup-tree-dump original } }
diff --git a/gcc/testsuite/g++.dg/ext/force_static/const.C b/gcc/testsuite/g++.dg/ext/force_static/const.C
new file mode 100644
index 0000000..5f2581f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/force_static/const.C
@@ -0,0 +1,7 @@ 
+class  __attribute__((force_static)) foo
+{
+public:
+  /* It's harmless to have a "const" qualifier on a method of
+     such a class.  */
+  void bar () const;
+};
diff --git a/gcc/testsuite/g++.dg/ext/force_static/ctor.C b/gcc/testsuite/g++.dg/ext/force_static/ctor.C
new file mode 100644
index 0000000..7d499ec
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/force_static/ctor.C
@@ -0,0 +1,5 @@ 
+class  __attribute__((force_static)) foo
+{
+public:
+  foo(); /* { dg-error "constructor cannot be static member function" } */
+};
diff --git a/gcc/testsuite/g++.dg/ext/force_static/dtor.C b/gcc/testsuite/g++.dg/ext/force_static/dtor.C
new file mode 100644
index 0000000..a62ba57
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/force_static/dtor.C
@@ -0,0 +1,5 @@ 
+class  __attribute__((force_static)) foo
+{
+public:
+  ~foo(); /* { dg-error "destructor cannot be static member function" } */
+};
diff --git a/gcc/testsuite/g++.dg/ext/force_static/method.C b/gcc/testsuite/g++.dg/ext/force_static/method.C
new file mode 100644
index 0000000..b94de84
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/force_static/method.C
@@ -0,0 +1,19 @@ 
+// { dg-options "-fdump-tree-original" }
+
+class  __attribute__((force_static)) foo
+{
+public:
+  int some_method (int i);
+
+private:
+  int some_field;
+};
+
+int foo::some_method (int i)
+{
+  some_field += i;
+  return some_field;
+}
+
+// { dg-final { scan-tree-dump-not "this" "original" } }
+// { dg-final { cleanup-tree-dump original } }
diff --git a/gcc/testsuite/g++.dg/ext/force_static/not-a-class.C b/gcc/testsuite/g++.dg/ext/force_static/not-a-class.C
new file mode 100644
index 0000000..0cda06c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/force_static/not-a-class.C
@@ -0,0 +1 @@ 
+int i __attribute__((force_static)); /* { dg-warning "'force_static' attribute can only be applied to class definitions" }  */
diff --git a/gcc/testsuite/g++.dg/ext/force_static/struct.C b/gcc/testsuite/g++.dg/ext/force_static/struct.C
new file mode 100644
index 0000000..8ba5a50
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/force_static/struct.C
@@ -0,0 +1,6 @@ 
+struct  __attribute__((force_static)) foo
+{
+  int i;
+};
+
+int foo::i;
diff --git a/gcc/testsuite/g++.dg/ext/force_static/trailing.C b/gcc/testsuite/g++.dg/ext/force_static/trailing.C
new file mode 100644
index 0000000..b6dfa2f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/force_static/trailing.C
@@ -0,0 +1,3 @@ 
+class foo
+{
+} __attribute__((force_static)); /* { dg-warning "'force_static' attribute must be applied after the class keyword, not after the closing brace" } */
diff --git a/gcc/testsuite/g++.dg/ext/force_static/vfunc.C b/gcc/testsuite/g++.dg/ext/force_static/vfunc.C
new file mode 100644
index 0000000..b0da35d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/force_static/vfunc.C
@@ -0,0 +1,5 @@ 
+class  __attribute__((force_static)) foo
+{
+public:
+  virtual void bar (); /* { dg-error "member 'bar' cannot be declared both virtual and static" } */
+};