Writing a MSVC-compatible vtable generator

Hello Peter, John,

As of r159090, Clang generated MSVC-incompatible vtables.
[Currently it generates the same intermediate objects but due to
r159091 they are not emitted into the object file at all]

I'd like to fix that and get MSVC-compatible vtables, vbtables etc.

Can you give some recommendations on how to achieve that?

VTableBuilder.{h,cpp} are 3K lines total which scares me a bit :slight_smile:

Options:
a) Duplicate a lot of code and have MSVTableComponent, MSVTableLayout,
MSVTableContext, MSVTableBuilder etc.
    and call different methods of different classes in the
{Itanium,Microsoft}CXXABI::EmitVTables
   Disadvantage: a LOT of code duplication
   Advantage: implementations are completely separate, no conflicts,
little chance of breaking the Itanium ABI.
   My opinion: don't like it.

b) Inject a virtual interface at some point and provide two
implementations (Itanium, Microsoft),
    similar to CGCXXABI vs ItaniumCXXABI vs MicrosoftCXXABI.
   If so, can you give your thoughts about which class should be abstracted out?
   Unfortunately, I'm not very familiar with the code.
   My best guess is that VTableLayout should be compatible with any
ABI (right?) and it's the VTableContext that should be converted into
an interface.
   It might be that case that VTableContext is compatible with any ABI
and it's VTableBuilder that should be converted into an interface.
   In any case, the current Itanium-specific parts of the
implementation should then be moved to ItaniumVTableXXX.

   Advantage: "the OOP way"
   Disadvantage: may make further progress harder if ItaniumVTableXXX
and MicrosoftVTableXX implementations are substantially different due
to different terminology/layout in the ABI.
   My opinion: the way to go, but the interface injection point should
be well thought of.

c) Add a bunch of "if (ABI == Itanium) {} else if (ABI == Microsoft)
{}" conditions to VTableBuilder.cpp
   It's ugly but works at least in the simple cases (e.g. non-virtual
inheritance)

   Advantage: very easy to fix simple cases
   Disadvantage: not "the OOP way" + may be the most complex way to
create a complete solution

Looking forward for your ideas!
Thanks,
Timur Iskhodzhanov,
Google Russia

Hi all.

Hello Peter, John,

As of r159090, Clang generated MSVC-incompatible vtables.
[Currently it generates the same intermediate objects but due to
r159091 they are not emitted into the object file at all]

I'd like to fix that and get MSVC-compatible vtables, vbtables etc.

Can you give some recommendations on how to achieve that?

VTableBuilder.{h,cpp} are 3K lines total which scares me a bit :slight_smile:

Options:
a) Duplicate a lot of code and have MSVTableComponent, MSVTableLayout,
MSVTableContext, MSVTableBuilder etc.
     and call different methods of different classes in the
{Itanium,Microsoft}CXXABI::EmitVTables
    Disadvantage: a LOT of code duplication
    Advantage: implementations are completely separate, no conflicts,
little chance of breaking the Itanium ABI.
    My opinion: don't like it.

b) Inject a virtual interface at some point and provide two
implementations (Itanium, Microsoft),
     similar to CGCXXABI vs ItaniumCXXABI vs MicrosoftCXXABI.
    If so, can you give your thoughts about which class should be abstracted out?
    Unfortunately, I'm not very familiar with the code.
    My best guess is that VTableLayout should be compatible with any
ABI (right?) and it's the VTableContext that should be converted into
an interface.
    It might be that case that VTableContext is compatible with any ABI
and it's VTableBuilder that should be converted into an interface.
    In any case, the current Itanium-specific parts of the
implementation should then be moved to ItaniumVTableXXX.

    Advantage: "the OOP way"
    Disadvantage: may make further progress harder if ItaniumVTableXXX
and MicrosoftVTableXX implementations are substantially different due
to different terminology/layout in the ABI.
    My opinion: the way to go, but the interface injection point should
be well thought of.

We can write some general interface - CGVTable(for example).
ItaniumVtable and MicrosoftVTable will implement this interface.
For some specific things we can do next thing:

class CGVtable {
// ...

virtual SomeSpecificInterface *get() = 0;

// ..
};

class MicrosoftVTable : CGVTable, SomeSpecificInterface {

virtual SomeSpecificInterface *get() { return this; }

};

This method works fine in our code for some microsoft specific mangling features.

c) Add a bunch of "if (ABI == Itanium) {} else if (ABI == Microsoft)
{}" conditions to VTableBuilder.cpp
    It's ugly but works at least in the simple cases (e.g. non-virtual
inheritance)

We have working code of vf-table & vb-table generation and we use this variant.
This check will be needed only in few places.
If you want to see how it done I can give you link to our github repo. Also you will find previous variant in our microsoft mangling code. Code is not very good, but it works.

    Advantage: very easy to fix simple cases
    Disadvantage: not "the OOP way" + may be the most complex way to
create a complete solution

Looking forward for your ideas!
Thanks,
Timur Iskhodzhanov,
Google Russia

- Dmitry Sokolov.

If you've found that this is working for the full generality, then great. I just didn't want us to commit to an interface which was going to immediately fall apart when we needed to support vbtbls.

John.

We do not have enough time for testing. So I can`t say that this code fully correct it need more testing.
If MS RTTI info can be committed in trunk then I can start publish small patches with RTTI.
I think after that vf-table/vb-table implementation can be started.

  - Dmitry Sokolov.

Why do you need RTTI for v*tables?
I think we can start with no-RTTI

I’ll be able to help on this with reviewing and testing.

We can skip this step, but vf-table contains pointer to RTTI info. Also it can be done separately. In vf-table we can store nullptr for RTTI and when RTTI will be done we`ll put pointer to it.

Yeah that’s what I’ve meant - we can do RTTI later and avoid an extra dependency to make faster incremental progress