Translation is incorrect for destructors on Microsoft ABI #243
Labels
Blocks-PhysX
Bug
Concept-Correctness
Issues concerning the correctness of the translation from an ABI perspective
Concept-OutputUsability
Issues concerning whether or not the output from Biohazrd is actually usable
Platform-Windows
Issues specific to Windows
Workaround
Found this while investigating an issue with calling the destructor of
PxDefaultFileOutputStream
with Mochi.PhysX. (Ironically I apparently knew about this when I filed #210 but never put 2 and 2 together.)The ABI of (virtual?) destructors should include an
int
parameter that determines whether the destructor will calloperator delete
or not. This isn't being emitted so it's receiving whatever garbage is inedx
. Additionally this is making it impossible to properly call the destructor for stack-allocator objects (which obviously don't need to be deleted.)Godbolt for the below code.
Destructors in the Microsoft ABI
Consider the type below:
Currently Biohazrd exposes this constructor as
delegate* unmanaged<Hello*, void>
, but as mentioned above it's actuallydelegate* unmanaged<Hello*, int, void>
.Expressed as a C function, it would be something like
void DestroyHello(Hello* _this, int shouldDelete);
(Technically it returns
this
too. Although I'm not really sure why that's useful considering that pointer is now invalid.)The implementation of this destructor doesn't show up on Godbolt, so here it is for
PxDefaultFileOutputStream
:Basically this boils down to:
There are basically three ways to call the destructor from C++:
How destructors are called in Microsoft ABI
Implicit call for stack-allocated objects
MSVC currently devirtualizes the call to the destructor here (even with optimizations off) and calls
~Hello
directly without going through the deleting destructor. This is ideal from a performance perspective for stack-allocated objects since you know for sure what they are. It might not be safe or practical to expose this in C#. (I also don't think we'd be able to export it via an inline export helper in situations where that's needed.)Implicitish call when deleting heap-allocated objects
This basically compiles down to
if (x) DestroyHello(x, 1);
(see below), so theoperator_Delete
call in the assembly above gets called to deallocate the object. (As you might imagine, this is horribly invalid ifx
is not actually allocated on the heap controlled by thedelete
implementation.)Explicit call
You only ever really see this in C++ when placement new is being used. In the context of Biohazrd, it could be argued that basically all C++ objects allocated by managed code are allocated with placement new. (You can also kind-of look at stack allocation in C++ as a specialized variant of placement new.)
Both of these compile down to
DestroyHello(x, 0);
:Why function arrangement is failing here
In theory the arranged function should've caught this and emitted it with the
int
parameter, but it didn't (I double checked we weren't ignoring a special flag or something.)What I suspect is happening is that we're arranging the
Hello::~Hello
function rather than theHello::
vector deleting destructor'` function...Theory confirmed:
https://github.com/MochiLibraries/llvm-project/blob/c41801cb0b02eaed1db8ade850987b5c9f3ea3d6/clang/tools/libclang/PathogenExtensions.cpp#L1894
I believe we actually need to use
Dtor_Deleting
here.This does expose a slight conundrum though. How will we call the base destructor for destructors overridden from C#? Obviously we could call the deleting destructor with
shouldDelete = 0
, but that doesn't seem quite right. One of the destructor types isDtor_Base
, is that what this is?The text was updated successfully, but these errors were encountered: