Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does `std::make_shared` perform two separate allocations with `-fno-rtti`?

Tags:

#include <memory> struct foo { }; int main() { std::make_shared<foo>(); } 

The asssembly generated by both g++7 and clang++5 with -fno-exceptions -Ofast for the code above:

  • Contains a single call to operator new if -fno-rtti is not passed.

  • Contains two separate calls to operator new if -fno-rtti is passed.

This can be easily verified on gcc.godbolt.org (clang++5 version):

screenshot of the above godbolt link with highlighed operator new calls

Why is this happening? Why does disabling RTTI prevent make_shared from unifying the object and control block allocations?

like image 917
Vittorio Romeo Avatar asked Mar 29 '17 12:03

Vittorio Romeo


2 Answers

Why does disabling RTTI prevent make_shared from unifying the object and control block allocations?

You can see from the assembler (just pasting the text is really preferable to both linking and to taking pictures of it) that the unified version doesn't allocate a simple foo but an std::_Sp_counted_ptr_inplace, and further that that type has a vtable (recall it needs a virtual destructor in general, to cope with custom deleters)

mov QWORD PTR [rax], OFFSET FLAT:   vtable for   std::_Sp_counted_ptr_inplace<foo, std::allocator<foo>,   (__gnu_cxx::_Lock_policy)2>+16 

If you disable RTTI, it can't generate the inplace counted pointer because that needs to be virtual.

Note that the non-inplace version still refers to a vtable, but it seems to just be storing the de-virtualized destructor address directly.

like image 192
Useless Avatar answered Sep 20 '22 14:09

Useless


Naturally, std::shared_ptr will be implemented with the assumption of the compiler supporting rtti. But it can be implemented without it. See shared_ptr without RTTI? .

Taking a cue from this old GCC's libstdc++ #42019 bug. We can see that Jonathan Wakely added a fix to make this possible without RTTI.

In GCC's libstdc++, std::make_shared uses the services of std::allocated_shared which uses a non-standard constructor(as seen in the code, reproduced below).

As seen in this patch, from line 753, you can see that getting the default deleter simply requires using the services of typeid if RTTI is enabled, otherwise, it requires a separate allocation that doesn't depend on RTTI.

EDIT: 9 - May -2017: removed copyrighted code previously posted here

I haven't investigated libcxx, but I want to believe they did similar thing....

like image 29
WhiZTiM Avatar answered Sep 16 '22 14:09

WhiZTiM