Could we emit inline virtual member functions as available_externally when there's a key function (same as vtable and debug info)?

I assume there’s a good reason we don’t do this - but I can’t quite figure it out off the top of my head.

Any thoughts?

The translation unit that emits the key function is required to emit the v-table symbol with as a strong definition with the type's visibility. It is not required to emit visible symbols for any of the inline virtual functions. It is, of course, required to emit those functions as part of emitting the v-table; but it doesn't have to provide symbols for them at all, because even if it were possible to take the address of a virtual member function in a comparable way, there is no way to take that address *via the v-table* and so the v-table's reference may be to its own implementation. This is good because it allows virtual methods to be given hidden visibility by default; it also permits v-tables to contain specialized method implementations, e.g. methods that use a known most-derived class.

John.

To be clear, your idea would be a reasonable guarantee to make in a revised C++ ABI; it's just that it's not guaranteed to work under the current one.

John.

This problem was pretty annoying when I was working on devirtualization. It
would be cool to fix ABI to support that.

I’m curious - how did it come up as a problem in devirtualization?

Ah, right. Thanks for the context, John!

(in throwing this around with Chandler & Richard, we're also considering
about an even bigger (& user facing, not just ABI breaking) possibility:
(we discussed a few variants, but this was one of them) require that all
inline member functions originally declared inline be defined in any
translation unit that defines a non-inline member function (or perhaps just
virtual ones?) - and use that to emit weak_odr+available_externally
definitions of those inline functions (mostly because we have protobufs
with lots and lots of inline functions). I may try prototyping this to see
how valuable it is to us, then figure out the finer points if it is worth
pursuing.

This starts to look a lot like modules codegen would look like (where we
would build an object file from a module definition with weak_odr
definitions of any inline functions, with debug info as well as debug info
for any types defined there (like the current modules debug info), etc))

Sorry, my bad - there was a problem with available_externally vtables that we were unable to generate when there was a inline virtual function.

Sorry, my bad - there was a problem with available_externally vtables that
we were unable to generate when there was a inline virtual function.

Ah, I've found the code and some of the comments for that - do you remember
the details or have pointers to the related threads/reviews/etc that came
to that conclusion?

Could we not emit the inline functions as linkonce_odr as usual and then
emit the available_externally vtable on top of that. Presumably once we do
all the optimization and drop the vtable, any of these inline virtual
functions that haven't got any devirtualized calls to them will disappear
due to lacking any reference holding them alive.

It'd bloat object files a bit, but only in the cases where there's an
optimization win - right?

But I guess there's a reason that doesn't work/wasn't implemented.

- David

It's complicated. Here's an interesting test case where it is hard to
generate an available_externally vtable for C:

#include <memory>
struct A;
struct B {
  virtual void ~B();
};
struct C : B {
  C();
  virtual void f();
  std::unique_ptr<A> a;
};
C *getit() {
  C *p = new C;
  p->f();
  return p;
}

In order to emit an externally_available vtable, we need to emit all the
inline virtual functions as linkonce_odr, which requires semantically
checking them up front, which would implicitly define ~C, which would
instantiate ~unique_ptr<A>, which requires A to be complete.

The question is, are we allowed to require 'A' to be a complete type so
that we can emit the implicit definition of C::~C?

Blink/WebKit/Nico want the answer to be "no", and most compilers give that
answer, because there is no vtable reference required here to generate
code. Richard's interpretation of the standard says that the call to the
virtual function 'f' allows us to do the semantic checking, so we could
change our minds if we think it's worth the hassle of compatibility issues
and user confusion.

I think it will be very hard to explain to users why 'A' needs to be
complete, since there's no clear vtable reference. We would need a
diagnostic note saying that the vtable was required to be complete at this
virtual call to support devirtualization.

Well, alternatively we can meet in the middle & not apply this rule to
implicit virtual functions (the dtor being the only possible instance of
that, I think) if that's especially convenient/valuable.

Also, for now, I don't think we emit the available_externally vtable when
the ctor is out of line anyway (we only emit it when we emit the store of
the vtable pointer - in theory we could emit it elsewhere and use
llvm.assume after the ctor call to indicate that the vtable pointer does
now point to the vtable in question)

- Dave