[cfe-dev] modular codegen of class template static member variables

David Blaikie via cfe-dev cfe-dev at lists.llvm.org
Mon Nov 20 15:04:35 PST 2017

Hi Richard,

(Lang, you're here because I mentioned stumbling across this on Friday in
ORC - this is the reduced test case (where 't' is the NameMutex member and
'nt' is the Name member))

Working on getting all LLVM binaries linking successfully under modular
codegen, I've hit something that seems it'll need a bit more feature work
(which I'm happy/planning to do myself - though always happy for

The test case I have boils down to the following modular header:

  struct trivial {};
  struct nontrivial { nontrivial(); };
  // namespace foo {
  void sink(void *);
  template <typename T> struct bar {
    static void baz() {
    static trivial t;
    static nontrivial nt;
  template <typename T> trivial bar<T>::t;
  template <typename T> nontrivial bar<T>::nt;
  //} // namespace foo
  template struct bar<int>;
  // inline void use() { (void)bar<int>::baz(); }

To build with modular codegen:

  $ echo 'module foo { header "foo.h" }' > foo.cppmap
  $ clang++ -cc1 -xc++ -emit-module -fmodules -w -fmodule-name=foo
foo.cppmap -o foo.pcm -fmodules-codegen

So here are some interesting facts I know, some of which may be relevant,
some of which may not:

   1.  Code as written ends up with linkonce_odr definitions for t and nt
   2. Use use instead of the explicit instantiation and are both t and nt are
   only declarations
   3. Add the outer namespace foo and then t is emitted as a linkonce_odr
   definition and nt is emitted as a declaration

That last one (which was the first result I got) really confuses me - any
ideas why a namespace would change the behavior here?

In any case, all those mysteries/differences in behavior might be aside to
actually fixing the behavior here, which is what this email is really about.

This is basically the same problem as inline variables, and maybe even
would allow some support for static variables in headers too (not sure,
will see).

Any ideas what the behavior should be here? Since there's a desire not to
run all global initializers if their specific submodule header isn't
included in the program (for iostream's sake), how would this be done
correctly under modular codegen?
My initial thought is potentially to defer the global initializers to the
includers (that seems necessary to get the lazy/only-those-included
behavior, right?) But that may not account for indirect inclusion? I guess
that's already handled somehow for the iostreams non-modular case, so maybe
it works.

& then the modular object file would perhaps have the weak_odr definition
of the global variable itself, but no global initializer - depending on any
live codepaths that reference the global necessarily requiring the using TU
to have caused the initializer to run? That seems vaguely concerning...

Is this making sense? Any good ideas? Pointers to where to start, etc?

- Dave
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20171120/7a8b64c7/attachment.html>

More information about the cfe-dev mailing list