从REPLACEMENT_OPERATOR_NEW_AND_DELETE看UE的堆内存管理及gcc相关实现
观察
为了让庞大代码库看起来更简洁一些,UE使用了不少C/C++黑魔法:宏。把一些重复或者繁琐的实现细节隐藏在了宏里面(例如最为常见且繁琐的GENERATED_BODY宏),尽管代码看起来更简洁,但也隐藏了一些(重要的)细节。
在看UE插件实现时,意外的看到IMPLEMENT_MODULE宏定义中,不仅包含了初始化的InitializeModule接口,还包含了一个看起来更复杂的PER_MODULE_BOILERPLATE宏定义。
///@file: Engine\Source\Runtime\Core\Public\Modules\ModuleManager.h
#define IMPLEMENT_MODULE( ModuleImplClass, ModuleName ) \
\
/**/ \
/* InitializeModule function, called by module manager after this module's DLL has been loaded */ \
/**/ \
/* @return Returns an instance of this module */ \
/**/ \
extern "C" DLLEXPORT IModuleInterface* InitializeModule() \
{ \
return new ModuleImplClass(); \
} \
/* Forced reference to this function is added by the linker to check that each module uses IMPLEMENT_MODULE */ \
extern "C" void IMPLEMENT_MODULE_##ModuleName() { } \
PER_MODULE_BOILERPLATE \
PER_MODULE_BOILERPLATE_ANYLINK(ModuleImplClass, ModuleName)
这个宏的内容如下所示,可以看到主要是new/delete操作符的定义,这也就是说在每个模块的C++中都包含包含有一个这样的操作符实现。更重要的是:这是一个重载定义而没有重载声明,C++执行new/delete操作符时,编译器甚至不知道这个定义的存在,这种情况下会用到这个定义吗?会不会用到编译器的缺省实现(毕竟我们通常是不会自定义这些实现的)?
///@file: Engine\Source\Runtime\Core\Public\Modules\Boilerplate\ModuleBoilerplate.h
// Disable the replacement new/delete when running the Clang static analyzer, due to false positives in 15.0.x:
// https://github.com/llvm/llvm-project/issues/58820
#if !FORCE_ANSI_ALLOCATOR && !defined(__clang_analyzer__)
static_assert(__STDCPP_DEFAULT_NEW_ALIGNMENT__ <= 16, "Expecting 16-byte default operator new alignment - alignments > 16 may have bloat");
#define REPLACEMENT_OPERATOR_NEW_AND_DELETE \
OPERATOR_NEW_MSVC_PRAGMA void* operator new ( size_t Size ) OPERATOR_NEW_THROW_SPEC { return FMemory::Malloc( Size ? Size : 1, __STDCPP_DEFAULT_NEW_ALIGNMENT__ ); } \
OPERATOR_NEW_MSVC_PRAGMA void* operator new[]( size_t Size ) OPERATOR_NEW_THROW_SPEC { return FMemory::Malloc( Size ? Size : 1, __STDCPP_DEFAULT_NEW_ALIGNMENT__ ); } \
OPERATOR_NEW_MSVC_PRAGMA void* operator new ( size_t Size, const std::nothrow_t& ) OPERATOR_NEW_NOTHROW_SPEC { return FMemory::Malloc( Size ? Size : 1, __STDCPP_DEFAULT_NEW_ALIGNMENT__ ); } \
OPERATOR_NEW_MSVC_PRAGMA void* operator new[]( size_t Size, const std::nothrow_t& ) OPERATOR_NEW_NOTHROW_SPEC { return FMemory::Malloc( Size ? Size : 1, __STDCPP_DEFAULT_NEW_ALIGNMENT__ ); } \
OPERATOR_NEW_MSVC_PRAGMA void* operator new ( size_t Size, std::align_val_t Alignment ) OPERATOR_NEW_THROW_SPEC { return FMemory::Malloc( Size ? Size : 1, (std::size_t)Alignment ); } \
OPERATOR_NEW_MSVC_PRAGMA void* operator new[]( size_t Size, std::align_val_t Alignment ) OPERATOR_NEW_THROW_SPEC { return FMemory::Malloc( Size ? Size : 1, (std::size_t)Alignment ); } \
OPERATOR_NEW_MSVC_PRAGMA void* operator new ( size_t Size, std::align_val_t Alignment, const std::nothrow_t& ) OPERATOR_NEW_NOTHROW_SPEC { return FMemory::Malloc( Size ? Size : 1, (std::size_t)Alignment ); } \
OPERATOR_NEW_MSVC_PRAGMA void* operator new[]( size_t Size, std::align_val_t Alignment, const std::nothrow_t& ) OPERATOR_NEW_NOTHROW_SPEC { return FMemory::Malloc( Size ? Size : 1, (std::size_t)Alignment ); } \
void operator delete ( void* Ptr ) OPERATOR_DELETE_THROW_SPEC { FMemory::Free( Ptr ); } \
void operator delete[]( void* Ptr ) OPERATOR_DELETE_THROW_SPEC { FMemory::Free( Ptr ); } \
void operator delete ( void* Ptr, const std::nothrow_t& ) OPERATOR_DELETE_NOTHROW_SPEC { FMemory::Free( Ptr ); } \
void operator delete[]( void* Ptr, const std::nothrow_t& ) OPERATOR_DELETE_NOTHROW_SPEC { FMemory::Free( Ptr ); } \
void operator delete ( void* Ptr, size_t Size ) OPERATOR_DELETE_THROW_SPEC { FMemory::Free( Ptr ); } \
void operator delete[]( void* Ptr, size_t Size ) OPERATOR_DELETE_THROW_SPEC { FMemory::Free( Ptr ); } \
void operator delete ( void* Ptr, size_t Size, const std::nothrow_t& ) OPERATOR_DELETE_NOTHROW_SPEC { FMemory::Free( Ptr ); } \
void operator delete[]( void* Ptr, size_t Size, const std::nothrow_t& ) OPERATOR_DELETE_NOTHROW_SPEC { FMemory::Free( Ptr ); } \
void operator delete ( void* Ptr, std::align_val_t Alignment ) OPERATOR_DELETE_THROW_SPEC { FMemory::Free( Ptr ); } \
void operator delete[]( void* Ptr, std::align_val_t Alignment ) OPERATOR_DELETE_THROW_SPEC { FMemory::Free( Ptr ); } \
void operator delete ( void* Ptr, std::align_val_t Alignment, const std::nothrow_t& ) OPERATOR_DELETE_NOTHROW_SPEC { FMemory::Free( Ptr ); } \
void operator delete[]( void* Ptr, std::align_val_t Alignment, const std::nothrow_t& ) OPERATOR_DELETE_NOTHROW_SPEC { FMemory::Free( Ptr ); } \
void operator delete ( void* Ptr, size_t Size, std::align_val_t Alignment ) OPERATOR_DELETE_THROW_SPEC { FMemory::Free( Ptr ); } \
void operator delete[]( void* Ptr, size_t Size, std::align_val_t Alignment ) OPERATOR_DELETE_THROW_SPEC { FMemory::Free( Ptr ); } \
void operator delete ( void* Ptr, size_t Size, std::align_val_t Alignment, const std::nothrow_t& ) OPERATOR_DELETE_NOTHROW_SPEC { FMemory::Free( Ptr ); } \
void operator delete[]( void* Ptr, size_t Size, std::align_val_t Alignment, const std::nothrow_t& ) OPERATOR_DELETE_NOTHROW_SPEC { FMemory::Free( Ptr ); }
#else
#define REPLACEMENT_OPERATOR_NEW_AND_DELETE
#endif
allocator
和UE的很多功能一样,allocator的相关功能并没有详细文档,不过在EMemoryAllocatorToUse可以看到所有的支持的allocator列表。
enum EMemoryAllocatorToUse
{
Ansi,
Stomp,
TBB,
Jemalloc,
Binned,
Binned2,
Binned3,
Platform,
Mimalloc,
}
通过查看UE的代码可知在Unix系统下进程可以通过命令行设置-jemalloc,-ansimalloc,-binnedmalloc,-mimalloc,-binnedmalloc2等来订制分配器。
class FMalloc* FUnixPlatformMemory::BaseAllocator()
{
///...
// Allow overriding on the command line.
// We get here before main due to global ctors, so need to do some hackery to get command line args
if (FILE* CmdLineFile = fopen("/proc/self/cmdline", "r"))
{
char * Arg = nullptr;
size_t Size = 0;
while(getdelim(&Arg, &Size, 0, CmdLineFile) != -1)
{
#if PLATFORM_SUPPORTS_JEMALLOC
if (FCStringAnsi::Stricmp(Arg, "-jemalloc") == 0)
{
AllocatorToUse = EMemoryAllocatorToUse::Jemalloc;
break;
}
#endif // PLATFORM_SUPPORTS_JEMALLOC
if (FCStringAnsi::Stricmp(Arg, "-ansimalloc") == 0)
{
// see FPlatformMisc::GetProcessDiagnostics()
AllocatorToUse = EMemoryAllocatorToUse::Ansi;
break;
}
if (FCStringAnsi::Stricmp(Arg, "-binnedmalloc") == 0)
{
AllocatorToUse = EMemoryAllocatorToUse::Binned;
break;
}
#if MIMALLOC_ENABLED
if (FCStringAnsi::Stricmp(Arg, "-mimalloc") == 0)
{
AllocatorToUse = EMemoryAllocatorToUse::Mimalloc;
break;
}
#endif
if (FCStringAnsi::Stricmp(Arg, "-binnedmalloc2") == 0)
{
AllocatorToUse = EMemoryAllocatorToUse::Binned2;
break;
}
if (FCStringAnsi::Stricmp(Arg, "-fullcrashcallstack") == 0)
{
GFullCrashCallstack = true;
}
///...
}
///....
///...
// This was moved to the fact that we aboved the command line statements above to *include* other things besides allocator only switches
// Moving here allows the other globals to be set, while we override the ANSI allocator still no matter the command line options
if (FORCE_ANSI_ALLOCATOR)
{
AllocatorToUse = EMemoryAllocatorToUse::Ansi;
}
FMalloc * Allocator = NULL;
switch (AllocatorToUse)
{
case EMemoryAllocatorToUse::Ansi:
Allocator = new FMallocAnsi();
break;
#if WITH_MALLOC_STOMP
case EMemoryAllocatorToUse::Stomp:
Allocator = new FMallocStomp();
break;
#endif
#if PLATFORM_SUPPORTS_JEMALLOC
case EMemoryAllocatorToUse::Jemalloc:
Allocator = new FMallocJemalloc();
break;
#endif // PLATFORM_SUPPORTS_JEMALLOC
#if MIMALLOC_ENABLED
case EMemoryAllocatorToUse::Mimalloc:
Allocator = new FMallocMimalloc();
break;
#endif
case EMemoryAllocatorToUse::Binned2:
Allocator = new FMallocBinned2();
break;
default: // intentional fall-through
case EMemoryAllocatorToUse::Binned:
Allocator = new FMallocBinned(FPlatformMemory::GetConstants().BinnedPageSize & MAX_uint32, 0x100000000);
break;
}
#if UE_BUILD_DEBUG
printf("Using %s.\n", Allocator ? TCHAR_TO_UTF8(Allocator->GetDescriptiveName()) : "NULL allocator! We will probably crash right away");
#endif // UE_BUILD_DEBUG
#if UE_USE_MALLOC_REPLAY_PROXY
if (bAddReplayProxy)
{
Allocator = new FMallocReplayProxy(Allocator);
}
#endif // UE_USE_MALLOC_REPLAY_PROXY
return Allocator;
}
堆分配
除了基于C++的new/delete操作符之外,UE也必然会用到直接的堆内存操作。例如,最为典型的是从一个UClass创建一个UObject对象,由于UClass描述的对象只有在运行时才能获得,所以必然会涉及到堆内存的分配。尽管堆内存也可以通过类似于new char[SIZE]这种形式实现,但是毕竟看起来还是有些怪。
在真正的堆内存分配中,FUObjectAllocator::AllocateUObject内使用的也是前面在new函数中看到的FMemory::Malloc函数。也就是说明了一个简单的结论:UE是经过自定义的FMemory::Malloc函数(而不是标准C库的malloc)来实现堆内存管理的。
NewObject>>StaticConstructObject_Internal>>StaticAllocateObject==>>FUObjectAllocator::AllocateUObject
///@file: Engine\Source\Runtime\CoreUObject\Private\UObject\UObjectAllocator.cpp
/**
* Allocates a UObjectBase from the free store or the permanent object pool
*
* @param Size size of uobject to allocate
* @param Alignment alignment of uobject to allocate
* @param bAllowPermanent if true, allow allocation in the permanent object pool, if it fits
* @return newly allocated UObjectBase (not really a UObjectBase yet, no constructor like thing has been called).
*/
UObjectBase* FUObjectAllocator::AllocateUObject(int32 Size, int32 Alignment, bool bAllowPermanent)
{
// Force alignment to minimal of 16 bytes
Alignment = FMath::Max(16, Alignment);
int32 AlignedSize = Align( Size, Alignment );
UObjectBase* Result = nullptr;
bAllowPermanent &= PermanentObjectPool != nullptr;
const bool bPlaceInPerm = bAllowPermanent && (Align(PermanentObjectPoolTail,Alignment) + Size) <= (PermanentObjectPool + PermanentObjectPoolSize);
if (bAllowPermanent && !bPlaceInPerm)
{
// advance anyway so we can determine how much space we should set aside in the ini
uint8* AlignedPtr = Align( PermanentObjectPoolExceededTail, Alignment );
PermanentObjectPoolExceededTail = AlignedPtr + Size;
}
// Use object memory pool for objects disregarded by GC (initially loaded ones). This allows identifying their
// GC status by simply looking at their address.
if (bPlaceInPerm)
{
// Align current tail pointer and use it for object.
uint8* AlignedPtr = Align( PermanentObjectPoolTail, Alignment );
// Update tail pointer.
PermanentObjectPoolTail = AlignedPtr + Size;
Result = (UObjectBase*)AlignedPtr;
if (PermanentObjectPoolExceededTail < PermanentObjectPoolTail)
{
PermanentObjectPoolExceededTail = PermanentObjectPoolTail;
}
}
else
{
// Allocate new memory of the appropriate size and alignment.
Result = (UObjectBase*)FMemory::Malloc( Size, Alignment );
}
#if !UE_BUILD_TEST && !UE_BUILD_SHIPPING
checkf(IsAligned(Result, Alignment), TEXT("Allocated memory address does not match requirement of %d byte alignment for size %d"), Alignment, Size);
#endif
return Result;
}
C++标准
C++标准对于new/delete操作有些规定
replaceable allocation functions
[[nodiscard]] (since C++20)
void* operator new ( std::size_t count ); (1)
void* operator new[]( std::size_t count ); (2)
void* operator new ( std::size_t count, std::align_val_t al ); (3) (since C++17)
void* operator new[]( std::size_t count, std::align_val_t al ); (4) (since C++17)
replaceable non-throwing allocation functions
noexcept (since C++11)
[[nodiscard]] (since C++20)
void* operator new ( std::size_t count, const std::nothrow_t& tag );(5)
void* operator new[]( std::size_t count, const std::nothrow_t& tag );(6)
void* operator new ( std::size_t count,
std::align_val_t al, const std::nothrow_t& );(7) (since C++17)
void* operator new[]( std::size_t count,
std::align_val_t al, const std::nothrow_t& );(8) (since C++17)
non-allocating placement allocation functions
noexcept(since C++11)
[[nodiscard]](since C++20)
void* operator new ( std::size_t count, void* ptr ); (9)
void* operator new[]( std::size_t count, void* ptr ); (10)
user-defined placement allocation functions
void* operator new ( std::size_t count, user-defined-args... ); (11)
void* operator new[]( std::size_t count, user-defined-args... ); (12)
void* operator new ( std::size_t count,
std::align_val_t al, user-defined-args... ); (13) (since C++17)
void* operator new[]( std::size_t count,
std::align_val_t al, user-defined-args... );
和声明/定义相关的包括下面内容:
The versions (1-4) are implicitly declared in each translation unit even if the
header is not included. Versions (1-8) are replaceable: a user-provided non-member function with the same signature defined anywhere in the program, in any source file, replaces the default version. Its declaration does not need to be visible.
这里明确说明了1-4这个四个声明即使在没有include
这也就对应了UE中只是提供了这些函数的实现而没有提供对应声明的实现机制(其实使用者只需要包含
std库
在new文件的声明中可以看到,对于不在前面提到的(1-8)列表中的带placement的new(std::size_t, void* __p)等函数,它们是以inline的形式定义在头文件中的,所以如果包含了这个头文件,那么当然在编译的时候直接就内联了这个实现,所以就无法替换。
//@{
/** These are replaceable signatures:
* - normal single new and delete (no arguments, throw @c bad_alloc on error)
* - normal array new and delete (same)
* - @c nothrow single new and delete (take a @c nothrow argument, return
* @c NULL on error)
* - @c nothrow array new and delete (same)
*
* Placement new and delete signatures (take a memory address argument,
* does nothing) may not be replaced by a user's program.
*/
void* operator new(std::size_t) _GLIBCXX_THROW (std::bad_alloc)
__attribute__((__externally_visible__));
void* operator new[](std::size_t) _GLIBCXX_THROW (std::bad_alloc)
__attribute__((__externally_visible__));
void operator delete(void*) _GLIBCXX_USE_NOEXCEPT
__attribute__((__externally_visible__));
void operator delete[](void*) _GLIBCXX_USE_NOEXCEPT
__attribute__((__externally_visible__));
#if __cpp_sized_deallocation
void operator delete(void*, std::size_t) _GLIBCXX_USE_NOEXCEPT
__attribute__((__externally_visible__));
void operator delete[](void*, std::size_t) _GLIBCXX_USE_NOEXCEPT
__attribute__((__externally_visible__));
#endif
void* operator new(std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
__attribute__((__externally_visible__));
void* operator new[](std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
__attribute__((__externally_visible__));
void operator delete(void*, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
__attribute__((__externally_visible__));
void operator delete[](void*, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
__attribute__((__externally_visible__));
#if __cpp_aligned_new
void* operator new(std::size_t, std::align_val_t)
__attribute__((__externally_visible__));
void* operator new(std::size_t, std::align_val_t, const std::nothrow_t&)
_GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__));
void operator delete(void*, std::align_val_t)
_GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__));
void operator delete(void*, std::align_val_t, const std::nothrow_t&)
_GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__));
void* operator new[](std::size_t, std::align_val_t)
__attribute__((__externally_visible__));
void* operator new[](std::size_t, std::align_val_t, const std::nothrow_t&)
_GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__));
void operator delete[](void*, std::align_val_t)
_GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__));
void operator delete[](void*, std::align_val_t, const std::nothrow_t&)
_GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__));
#if __cpp_sized_deallocation
void operator delete(void*, std::size_t, std::align_val_t)
_GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__));
void operator delete[](void*, std::size_t, std::align_val_t)
_GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__));
#endif // __cpp_sized_deallocation
#endif // __cpp_aligned_new
// Default placement versions of operator new.
inline void* operator new(std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
{ return __p; }
inline void* operator new[](std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
{ return __p; }
// Default placement versions of operator delete.
inline void operator delete (void*, void*) _GLIBCXX_USE_NOEXCEPT { }
inline void operator delete[](void*, void*) _GLIBCXX_USE_NOEXCEPT { }
//@}
而对于可替换的版本,在std库实现中添加了weak属性,所以如果用户定义了自己的实现版本,则C++库的实现不会生效。
_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc)
{
void *p;
/* malloc (0) is unpredictable; avoid it. */
if (sz == 0)
sz = 1;
while (__builtin_expect ((p = malloc (sz)) == 0, false))
{
new_handler handler = std::get_new_handler ();
if (! handler)
_GLIBCXX_THROW_OR_ABORT(bad_alloc());
handler ();
}
return p;
}
不过在具体实现上,由于只有Darwin系统定义了_GLIBCXX_WEAK_DEFINITION宏为__attribute__ ((weak)),其它平台没有定义,所以这个weak其实是利用libstdc++.so的依赖顺序通常靠后,所以最后被使用来实现。
tsecer@harry: readelf -s /usr/lib64/libstdc++.so.6| c++filt -t | fgrep new
75: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __newlocale@GLIBC_2.2.5 (41)
120: 0000000000000000 0 NOTYPE WEAK DEFAULT UND transaction clone for operator new[](unsigned long)
245: 000000000008e550 18 FUNC GLOBAL DEFAULT 12 _ZNSt20bad_array_new_leng@@CXXABI_1.3.8
373: 000000000037c7d0 24 OBJECT WEAK DEFAULT 22 _ZTISt20bad_array_new_len@@CXXABI_1.3.8
591: 0000000000090ab0 5 FUNC GLOBAL DEFAULT 12 operator new[](unsigned long, std::align_val_t)@@CXXABI_1.3.11
606: 000000000008e530 19 FUNC GLOBAL DEFAULT 12 _ZNSt20bad_array_new_leng@@CXXABI_1.3.8
853: 0000000000090910 8 FUNC GLOBAL DEFAULT 12 std::get_new_handler()@@GLIBCXX_3.4.20
947: 0000000000090a00 132 FUNC GLOBAL DEFAULT 12 operator new(unsigned long, std::align_val_t)@@CXXABI_1.3.11
1841: 0000000000090980 88 FUNC GLOBAL DEFAULT 12 operator new(unsigned long, std::nothrow_t const&)@@GLIBCXX_3.4
2458: 00000000000909e0 5 FUNC GLOBAL DEFAULT 12 operator new[](unsigned long)@@GLIBCXX_3.4
2914: 0000000000091550 27 FUNC GLOBAL DEFAULT 12 __cxa_vec_new@@CXXABI_1.3
3100: 000000000008e530 19 FUNC GLOBAL DEFAULT 12 _ZNSt20bad_array_new_leng@@CXXABI_1.3.8
3336: 0000000000091450 244 FUNC GLOBAL DEFAULT 12 __cxa_vec_new2@@CXXABI_1.3
3340: 0000000000091570 254 FUNC GLOBAL DEFAULT 12 __cxa_vec_new3@@CXXABI_1.3
3367: 000000000037c7e8 40 OBJECT WEAK DEFAULT 22 _ZTVSt20bad_array_new_len@@CXXABI_1.3.8
3802: 00000000000909f0 5 FUNC GLOBAL DEFAULT 12 operator new[](unsigned long, std::nothrow_t const&)@@GLIBCXX_3.4
3991: 000000000008e520 8 FUNC GLOBAL DEFAULT 12 _ZNKSt20bad_array_new_len@@CXXABI_1.3.8
4311: 000000000013d3b0 25 OBJECT WEAK DEFAULT 14 _ZTSSt20bad_array_new_len@@CXXABI_1.3.8
4350: 0000000000090920 92 FUNC GLOBAL DEFAULT 12 operator new(unsigned long)@@GLIBCXX_3.4
4852: 000000000008eff0 50 FUNC GLOBAL DEFAULT 12 __cxa_throw_bad_array_new@@CXXABI_1.3.8
5346: 0000000000090900 11 FUNC GLOBAL DEFAULT 12 _ZSt15set_new_handlerPFvv@@GLIBCXX_3.4
tsecer@harry:
编译器内置声明
在gcc初始化声明时,有很大一部分代码在处理new/delete的这种内置声明。这里编译器纯手工(而不是基于程序分析)搓出了new/delete的相关声明。
///@file: gcc-7.3.1\gcc\cp\decl.c
/* Create the predefined scalar types of C,
and some nodes representing standard constants (0, 1, (void *)0).
Initialize the global binding level.
Make definitions for built-in primitive functions. */
void
cxx_init_decl_processing (void)
{
///...
{
tree newattrs, extvisattr;
tree newtype, deltype;
tree ptr_ftype_sizetype;
tree new_eh_spec;
ptr_ftype_sizetype
= build_function_type_list (ptr_type_node, size_type_node, NULL_TREE);
if (cxx_dialect == cxx98)
{
tree bad_alloc_id;
tree bad_alloc_type_node;
tree bad_alloc_decl;
push_namespace (std_identifier);
bad_alloc_id = get_identifier ("bad_alloc");
bad_alloc_type_node = make_class_type (RECORD_TYPE);
TYPE_CONTEXT (bad_alloc_type_node) = current_namespace;
bad_alloc_decl
= create_implicit_typedef (bad_alloc_id, bad_alloc_type_node);
DECL_CONTEXT (bad_alloc_decl) = current_namespace;
pop_namespace ();
new_eh_spec
= add_exception_specifier (NULL_TREE, bad_alloc_type_node, -1);
}
else
new_eh_spec = noexcept_false_spec;
/* Ensure attribs.c is initialized. */
init_attributes ();
/* Ensure constraint.cc is initialized. */
init_constraint_processing ();
extvisattr = build_tree_list (get_identifier ("externally_visible"),
NULL_TREE);
newattrs = tree_cons (get_identifier ("alloc_size"),
build_tree_list (NULL_TREE, integer_one_node),
extvisattr);
newtype = cp_build_type_attribute_variant (ptr_ftype_sizetype, newattrs);
newtype = build_exception_variant (newtype, new_eh_spec);
deltype = cp_build_type_attribute_variant (void_ftype_ptr, extvisattr);
deltype = build_exception_variant (deltype, empty_except_spec);
tree opnew = push_cp_library_fn (NEW_EXPR, newtype, 0);
DECL_IS_MALLOC (opnew) = 1;
DECL_IS_OPERATOR_NEW (opnew) = 1;
opnew = push_cp_library_fn (VEC_NEW_EXPR, newtype, 0);
DECL_IS_MALLOC (opnew) = 1;
DECL_IS_OPERATOR_NEW (opnew) = 1;
push_cp_library_fn (DELETE_EXPR, deltype, ECF_NOTHROW);
push_cp_library_fn (VEC_DELETE_EXPR, deltype, ECF_NOTHROW);
if (flag_sized_deallocation)
{
/* Also push the sized deallocation variants:
void operator delete(void*, std::size_t) throw();
void operator delete[](void*, std::size_t) throw(); */
tree void_ftype_ptr_size
= build_function_type_list (void_type_node, ptr_type_node,
size_type_node, NULL_TREE);
deltype = cp_build_type_attribute_variant (void_ftype_ptr_size,
extvisattr);
deltype = build_exception_variant (deltype, empty_except_spec);
push_cp_library_fn (DELETE_EXPR, deltype, ECF_NOTHROW);
push_cp_library_fn (VEC_DELETE_EXPR, deltype, ECF_NOTHROW);
}
if (aligned_new_threshold)
{
push_namespace (std_identifier);
tree align_id = get_identifier ("align_val_t");
align_type_node = start_enum (align_id, NULL_TREE, size_type_node,
NULL_TREE, /*scoped*/true, NULL);
pop_namespace ();
/* operator new (size_t, align_val_t); */
newtype = build_function_type_list (ptr_type_node, size_type_node,
align_type_node, NULL_TREE);
newtype = cp_build_type_attribute_variant (newtype, newattrs);
newtype = build_exception_variant (newtype, new_eh_spec);
opnew = push_cp_library_fn (NEW_EXPR, newtype, 0);
DECL_IS_MALLOC (opnew) = 1;
DECL_IS_OPERATOR_NEW (opnew) = 1;
opnew = push_cp_library_fn (VEC_NEW_EXPR, newtype, 0);
DECL_IS_MALLOC (opnew) = 1;
DECL_IS_OPERATOR_NEW (opnew) = 1;
/* operator delete (void *, align_val_t); */
deltype = build_function_type_list (void_type_node, ptr_type_node,
align_type_node, NULL_TREE);
deltype = cp_build_type_attribute_variant (deltype, extvisattr);
deltype = build_exception_variant (deltype, empty_except_spec);
push_cp_library_fn (DELETE_EXPR, deltype, ECF_NOTHROW);
push_cp_library_fn (VEC_DELETE_EXPR, deltype, ECF_NOTHROW);
if (flag_sized_deallocation)
{
/* operator delete (void *, size_t, align_val_t); */
deltype = build_function_type_list (void_type_node, ptr_type_node,
size_type_node, align_type_node,
NULL_TREE);
deltype = cp_build_type_attribute_variant (deltype, extvisattr);
deltype = build_exception_variant (deltype, empty_except_spec);
push_cp_library_fn (DELETE_EXPR, deltype, ECF_NOTHROW);
push_cp_library_fn (VEC_DELETE_EXPR, deltype, ECF_NOTHROW);
}
}
nullptr_type_node = make_node (NULLPTR_TYPE);
TYPE_SIZE (nullptr_type_node) = bitsize_int (GET_MODE_BITSIZE (ptr_mode));
TYPE_SIZE_UNIT (nullptr_type_node) = size_int (GET_MODE_SIZE (ptr_mode));
TYPE_UNSIGNED (nullptr_type_node) = 1;
TYPE_PRECISION (nullptr_type_node) = GET_MODE_BITSIZE (ptr_mode);
if (abi_version_at_least (9))
SET_TYPE_ALIGN (nullptr_type_node, GET_MODE_ALIGNMENT (ptr_mode));
SET_TYPE_MODE (nullptr_type_node, ptr_mode);
record_builtin_type (RID_MAX, "decltype(nullptr)", nullptr_type_node);
nullptr_node = build_int_cst (nullptr_type_node, 0);
}
///...
}
wrap up
尽管在UE的代码中没有看到直接的new/delete全局操作符声明和定义,但是由于每个module通过REPLACEMENT_OPERATOR_NEW_AND_DELETE间接定义了new/delete,基于C++对于new/delete操作符的相关规定,最终达到使用的还是UE自定义的堆管理机制。