[llvm] r188082 - DebugInfo: provide the ability to add members to a class after it has been constructed

Robinson, Paul Paul_Robinson at playstation.sony.com
Mon Aug 12 13:12:01 PDT 2013


> -----Original Message-----
> From: David Blaikie [mailto:dblaikie at gmail.com]
> Sent: Monday, August 12, 2013 11:56 AM
> To: Robinson, Paul
> Cc: llvm-commits at cs.uiuc.edu
> Subject: Re: [llvm] r188082 - DebugInfo: provide the ability to add
> members to a class after it has been constructed
> 
> On Mon, Aug 12, 2013 at 11:19 AM, Robinson, Paul
> <Paul_Robinson at playstation.sony.com> wrote:
> >>
> >>
> >> From: David Blaikie [mailto:dblaikie at gmail.com]
> >> Sent: Saturday, August 10, 2013 12:52 PM
> >> To: Robinson, Paul
> >> Cc: llvm-commits at cs.uiuc.edu
> >> Subject: Re: [llvm] r188082 - DebugInfo: provide the ability to add
> members to a class after it has been constructed
> >>
> >>
> >>
> >> On Fri, Aug 9, 2013 at 11:23 AM, Robinson, Paul
> >>  <Paul_Robinson at playstation.sony.com> wrote:
> >> > > -----Original Message-----
> >> > > From: llvm-commits-bounces at cs.uiuc.edu [mailto:llvm-commits-
> >> > > bounces at cs.uiuc.edu] On Behalf Of David Blaikie
> >> > > Sent: Friday, August 09, 2013 10:17 AM
> >> > > To: llvm-commits at cs.uiuc.edu
> >> > > Subject: [llvm] r188082 - DebugInfo: provide the ability to add
> members
> >> > > to a class after it has been constructed
> >> > >
> >> > > Author: dblaikie
> >> > > Date: Fri Aug  9 12:17:12 2013
> >> > > New Revision: 188082
> >> > >
> >> > > URL: http://llvm.org/viewvc/llvm-project?rev=188082&view=rev
> >> > > Log:
> >> > > DebugInfo: provide the ability to add members to a class after it
> has
> >> > > been constructed
> >> > >
> >> > > This is necessary to allow Clang to only emit implicit members
> when
> >> > > there is code generated for them, rather than whenever they are
> ODR
> >> > > used.
> >> > Um, so classes could have different member lists in different TUs,
> >> > meaning if you're using type units, the type units won't match?
> >>
> >> This has always been true of C++ due to
> >>
> >> 1) nested types:
> >>
> >> struct foo {
> >>  struct bar;
> >> };
> >>
> >> One TU has struct foo::bar { }; and another TU doesn't (this is
> common
> >> in the pimpl idiom) - the DIE for 'foo' can't be the same between
> those
> >> TUs since one will contain a declaration of 'bar' and another a
> definition
> >> (& in fact both Clang and GCC don't even emit the declaration of
> 'bar'
> >> if it's unreferenced in the current TU - so you'll get 3 versions: no
> >> mention of 'bar', a declaration of 'bar', and a definition of 'bar').
> We
> >> could possibly use out-of-line definitions the same way we do for
> function
> >> definitions, but I doubt GDB/debuggers could handle that just yet.
> >
> > With pimpl I'd expect only one TU with a definition so not matching
> the
> > declaration is okay.
> 
> Not sure what definition (I can think of a few) of "okay" you are using.
> 
> For what it's worth - GCC & Clang produce this in many more cases than
> just the classic pimpl - since both compiler's don even emit a
> declaration of the nested type in TUs that don't reference it. So
> you'll easily get a bunch of TUs without the nested type, some with a
> declaration, some with the definition. These could be considered bugs
> & we could say these debuggers should emit the full definition (or
> declaration if that's all that's available) in any TU that provides it
> whenever emitting the outer type. I'm not sure that'd be the best way
> forward.
> 
> > That is, yes, you get two copies in different TUs.
> 
> Given the above description of the way things are today it'll
> potentially be 3 for every nested member type & all the combinations
> if you have more than one (so if you've got two nested members it
> wouldn't be hard to imagine a total of 9 different versions of the
> outer type, etc...).
> 

First off, if you're doing type units, you should not omit any 
contents known to the TU, because you are creating a type 
declaration to be used by all TUs, and you don't know which 
declarations are needed in other TUs. That seems pretty fundamental 
to supporting type units.  Omitting some user-supplied declaration 
just because _this_ TU doesn't use it does seem like a bug.

But, looking closely at the computation of the type signature as
specified in DWARF v4, a nested type contributes only its name; the 
members of the nested type are not included(!).  So it's irrelevant 
whether the nested type is fully declared, the type signature is the 
same. Ditto for member functions, only the name contributes to the
type signature.

> >> 2) implicit special members, as mentioned in the Clang side of this
> change:
> >>
> >> struct foo { };
> >>
> >> in a TU with only that code, 'foo' does not contain a declaration for
> any
> >> of the 5 special members (default ctor, copy ctor, move ctor, copy
> assign,
> >> move assign, dtor). Only in a TU that references those special
> members
> >> would they be declared and then possibly defined (if they are ODR-
> used (eg:
> >> used from an evaluated context, not just "sizeof", etc))). The
> compiler
> >> isn't allowed to speculate about the existence/viability of creating
> >> declarations of those special members in a TU that doesn't reference
> them
> >> in any way.
> >>
> >> This was already true of Clang prior to this change - a TU that never
> used
> >> the special members would not have declarations of them, some other
> TU that
> >> did reference them would have them.
> >
> > Makes me think these artificial members should not contribute to the
> type
> > signature. They are of little to no value in the debugger anyhow.
> 
> They're necessary for correct backtraces, so how would we ensure we
> can describe this code?

You don't omit the artificial members from the type unit, you just
don't include them in the data contributing to the type _signature_.
So you will have type units in different TUs with matching signatures,
but they may vary in the exact set of artificial members included in
the class.

> 
> (something I don't know: how do we ensure the address references
> (hi/lo_pc) make sense when using type units? If we pick a random copy
> of the type it might refer to instances of the functions that aren't
> emitted in this TU (I suppose then it'll have a different type hash?)

The type unit that the linker picks (out of all the duplicates) is
associated with some TU. The addresses in the type unit are correct
for the code in that TU.  If some function isn't emitted in that TU
then the associated declaration in the type unit won't have hi/lo PC
addresses in it. (Attributes with addresses do not contribute to the
type signature.)

Backtraces through some other TU with its own copies of functions
won't be able to identify debug info for those functions (although
GDB can still find the object-file symbol and even demangle it for 
you).  If this hurts you, don't use type units.  Or get a linker
that can de-duplicate properly.

> Do type units break down when there's a reasonable proliferation of
> inline functions because each TU will have a different type
> signature?)

No, member functions contribute only their names to the type signature
so whether they're inlined (or call inlined functions) is irrelevant.

> 
> > Maybe ALL artificial members should not contribute to the type
> signature...
> >
> >>
> >> 3) member function template specializations
> >>
> >> struct foo { template<typename T> void func(); };
> >>
> >> In this case it's completely unbounded - 'func' could be instantiated
> with
> >> any number of types. There's no way we could even speculate about
> which
> >> members 'foo'f might have in total.
> >>
> >> GCC and Clang both simply emit declarations (and, optionally,
> definitions)
> >> of any specializations that 'func' has in this TU. GCC already only
> did
> >> this for specializations that actually generated code (because they
> were
> >> used by code that was being emitted) but Clang emitted it for every
> >> specialization.
> >>
> >> eg:
> >>
> >> inline void f() {
> >>  foo().func<int>();
> >> }
> >>
> >> int main() {
> >>  foo().func<float>();
> >> }
> >>
> >> produced two specializations (foo<int> only as a declaration with no
> >> definition, foo<float> with both declaration and definition) from
> Clang,
> >> but only one from GCC. Clang now produces only one in the above
> program.
> >> But as you can see, there could be any number of other instances of
> this
> >> member function template (foo<bool>, foo<string>, foo<bar>, etc) in
> other
> >> translation units.
> >
> > I knew about member templates, I think such classes shouldn't be
> considered
> > candidates for type units.  They aren't common.
> 
> Perhaps.

Heh.  If member functions contribute to the type signature only with
their names... and the names aren't qualified by the actual template
parameters... then the instantiations don't count as distinct members,
for type signature purposes (?).

--paulr






More information about the llvm-commits mailing list