MochiLibraries / Biohazrd

A framework for automatically generating binding wrappers for C/C++ libraries
MIT License
60 stars 9 forks source link

Add support for initializing vtable pointer of types without constructors #31

Open PathogenDavid opened 4 years ago

PathogenDavid commented 4 years ago

For types without constructors, the C++ compiler may initialize the vtable pointer wherever the type is initialized.

This was encountered with PxDefaultAllocator.

Here's a simple example: (Godbolt)

class AbstractBase
{
public:
    virtual void* allocate(int size);
};

class DefaultImpl : public AbstractBase
{
public:
    void* allocate(int size)
    {
        return nullptr;
    }
};

void UseAllocator(AbstractBase* allocator)
{
    allocator->allocate(100);
}

void Test()
{
    DefaultImpl impl;
    UseAllocator(&impl);
}

With GCC x86-64 10.2, Test is generated as follows:

Test():
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     eax, OFFSET FLAT:vtable for DefaultImpl+16
        mov     QWORD PTR [rbp-8], rax
        lea     rax, [rbp-8]
        mov     rdi, rax
        call    UseAllocator(AbstractBase*)
        nop
        leave
        ret

The easiest solution here is probably to use C trampolines for object construction (when necessary?)

PathogenDavid commented 4 years ago

Some C++ compilers will sometimes generate an implicit constructor to handle this.

For example, x86-64 clang 10.0.0 on Godbolt produces:

Test():                               # @Test()
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        lea     rdi, [rbp - 8]
        call    DefaultImpl::DefaultImpl() [base object constructor]
        lea     rax, [rbp - 8]
        mov     rdi, rax
        call    UseAllocator(AbstractBase*)
        add     rsp, 16
        pop     rbp
        ret

and x64 msvc v19.27:

impl$ = 32
void Test(void) PROC                                 ; Test
$LN3:
        sub     rsp, 56                             ; 00000038H
        lea     rcx, QWORD PTR impl$[rsp]
        call    DefaultImpl::DefaultImpl(void)
        lea     rcx, QWORD PTR impl$[rsp]
        call    void UseAllocator(AbstractBase *)  ; UseAllocator
        add     rsp, 56                             ; 00000038H
        ret     0
void Test(void) ENDP                                 ; Test
PathogenDavid commented 2 years ago

This is blocking the use of PxDefaultAllocator in PhysX. We should just leverage the inline export helper infrastructure for this.

PathogenDavid commented 2 years ago

Turns out this will be a bit of a pain since we can't synthesize TranslatedFunctions and InlineExportHelper assumes we have a `ClanSharp.FunctionDecl.

We'll probably need to add synthesizing functions (which is very odd outside this situation) as well as special-case for generating these trampolines. It might also be worth checking if Clang is internally synthesizing a constructor declaration or not so we can maybe avoid doing it ourselves.

PathogenDavid commented 2 years ago

I added a warning to CSharpTranslationVerifier to help alert people to this missing feature and to make it easier to enumerate the types affected by it. (Although ideally we should just add this sooner rather than later.)