Placement of vtables, inlines and such

Jason Merrill jason at cygnus.com
Fri Jun 25 05:28:03 UTC 1999


>>>>> Christophe de Dinechin <ddd at cup.hp.com> writes:

 > I think that the name of the vtable has to either contain the name  
 > of the class or the (mangled) name of the key function. If there is a  
 > key function, it does not harm to use that as the name (except that  
 > it makes for a slightly longer symbol), and it sometimes catches some  
 > broken makefiles that fail to recompile some .o file when a .h file  
 > they include changed :-). I don't know if catching a problem  
 > "sometimes" is better than "never", or if it is actually worse...

Worse, I think.  The heuristic can give different results in different
translation units for well-formed code.

 > Also, HP uses COMDATs for the actual emission of the vtable,  
 > out-of-line copies of inline functions, some cases of template  
 > instantiations, etc. Our COMDAT key is the name alone. I don't think  
 > we can have the linker check size or whatever. Note that it's hard to  
 > believe that the content of a COMDAT section for code emitted by two  
 > different compilers would have anything in common :-)

No, the size check is primarily interesting for vtables.

 >> I propose that the ia64 base ABI be extended to provide for either
 >> COMDAT sections or garbage collection, and that we use that support for
 >> vague linkage.

 > It looks to me like garbage collection requires one extra operation,  
 > namely selecting the "blessed" symbol that will not be discarded,  
 > right? Isn't that some other form of COMDATing? In other words, isn't  
 > garbage collection just an additional optimization which may be  
 > placed on top of COMDATs?

No.  gc works by sweeping from main, finding all the referenced symbols;
any sections that haven't provided any symbols are discarded.  If we use
weak symbols for vtables and put them in separate sections, gc will keep at
most one copy, as needed.  There is no notion of a special symbol.

gc is a more general solution, as it also can discard normal code that
isn't actually needed.  It also allows us to discard all copies in cases
where that is appropriate, such as when we can get the vtable from one of
our shared libs.  But COMDAT is simpler to implement, and has fewer
implications for the broader ABI.

 >> I further propose that we not use heuristics to cut down the number of
 >> copies ahead of time; they usually work fine, but can cause problems in
 >> some situations, such as when not all of the class's members are in the
 >> same symbol space.  Does the ia64 ABI provide for controlling which
 >> symbols are exported from a shared library?

 > As you said, "they usually work fine". In particular, without them,  
 > you end up emitting the same vtables again and again, which is a  
 > waste of time, disk space, etc. Just as a reminder, IA64 .o files are  
 > not exactly small.

True enough.

 > Regarding the symbol spaces, did I misunderstand, or are you talking
 > about having some pathological case like (using Microsoft's notations
 > :-)

 > 	#if INCLUDED_FROM_FILE_1
 > 	__declspec(export)
 > 	#endif
 > 	inline void foo() { ... }

 > That's just a bad idea. There may be other cases I did not think of,  
 > but currently, I don't see this as a real issue.

Not exactly.  The idea is not that the function needs to be explicitly
imported, but that it is simply unavailable.  A customer of ours defines a
proxy class where the main functionality is in one shared object which does
not export the functions; the functionality of the class is only made
available through virtual function calls.  Our use of the heuristic caused
us to give an undefined symbol for a client's call to one of the class'
inline functions.

It may be that we don't want to support this practice, but it is something
to think about.

 >> A side issue: What do we want to do with dynamically-initialized
 >> variables?  The same thing, or use COMMON?  I propose COMMON.

 > The problem is that some compiler may be smarter at inlining that  
 > another, and figure out that it actually can initialize it  
 > statically. In that case, it cannot go into COMMON (or you force that  
 > smart compiler to not do that optimization for binary compatibility  
 > reasons...). For instance:

 > 	inline int f() { return 1; }
 > 	static int i = f();

 > What is the problem with COMMON?

Do you mean "with COMDAT" here?  There's no problem; it doesn't really
matter which you use.  If one implementation uses COMMON, and another uses
COMDAT, they will be combined by the linker (I think; does a weak symbol
take precedence over COMMON?)

This does bring up another issue; handling initialization.  g++ handles
initialization of a weak/COMDAT object by emitting a sentry along with it,
which gets set when the object has been initialized.

BTW, note that this really only applies to template static data members;
that's the only case where you would find a dynamically initialized
variable with comdat linkage.

 >> A side issue is how to handle local static variables in inlines.  G++ 
 >> currently avoids this issue by suppressing inlining of functions with 
 >> local statics.  If we don't want to do that, we'll need to specify a 
 >> mangling for the statics, and handle multiple copies like we do above. 

 > Side issue of side issue: you are also supposed to name string  
 > constants, because they are supposed to have the same address in  
 > different inline functions ([7.1.2]/4 :-)

Aha.  I guess there's no point in trying to avoid dealing with local
statics then.

Jason




More information about the cxx-abi-dev mailing list