diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index b728917fdda6d..d284df29f3b33 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -3245,7 +3245,7 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction( return false; const auto *FPT = getType()->castAs(); - if (FPT->getNumParams() == 0 || FPT->getNumParams() > 3 || FPT->isVariadic()) + if (FPT->getNumParams() == 0 || FPT->getNumParams() > 4 || FPT->isVariadic()) return false; // If this is a single-parameter function, it must be a replaceable global @@ -3280,8 +3280,8 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction( *AlignmentParam = Params; } - // Finally, if this is not a sized delete, the final parameter can - // be a 'const std::nothrow_t&'. + // If this is not a sized delete, the next parameter can be a + // 'const std::nothrow_t&'. if (!IsSizedDelete && !Ty.isNull() && Ty->isReferenceType()) { Ty = Ty->getPointeeType(); if (Ty.getCVRQualifiers() != Qualifiers::Const) @@ -3293,6 +3293,19 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction( } } + // Finally, recognize the not yet standard versions of new that take a + // hot/cold allocation hint (__hot_cold_t). These are currently supported by + // tcmalloc (see + // https://github.com/google/tcmalloc/blob/220043886d4e2efff7a5702d5172cb8065253664/tcmalloc/malloc_extension.h#L53). + if (!IsSizedDelete && !Ty.isNull() && Ty->isEnumeralType()) { + QualType T = Ty; + while (const auto *TD = T->getAs()) + T = TD->getDecl()->getUnderlyingType(); + IdentifierInfo *II = T->getAs()->getDecl()->getIdentifier(); + if (II && II->isStr("__hot_cold_t")) + Consume(); + } + return Params == FPT->getNumParams(); } diff --git a/clang/test/CodeGenCXX/new_hot_cold.cpp b/clang/test/CodeGenCXX/new_hot_cold.cpp new file mode 100644 index 0000000000000..014e815201485 --- /dev/null +++ b/clang/test/CodeGenCXX/new_hot_cold.cpp @@ -0,0 +1,130 @@ +// Test to check that the appropriate attributes are added to the __hot_cold_t +// versions of operator new. + +// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -faligned-allocation -emit-llvm -o - | FileCheck %s + +typedef __typeof__(sizeof(0)) size_t; + +namespace std { +struct nothrow_t {}; +enum class align_val_t : size_t; +} // namespace std +std::nothrow_t nothrow; + +typedef unsigned char uint8_t; + +// See the following link for how this type is declared in tcmalloc: +// https://github.com/google/tcmalloc/blob/220043886d4e2efff7a5702d5172cb8065253664/tcmalloc/malloc_extension.h#L53. +enum class __hot_cold_t : uint8_t; + +// Test handling of declaration that uses a type alias, ensuring that it still +// recognizes the expected __hot_cold_t type name. +namespace malloc_namespace { +using hot_cold_t = __hot_cold_t; +} + +void *operator new(size_t size, + malloc_namespace::hot_cold_t hot_cold) noexcept(false); +void *operator new[](size_t size, + malloc_namespace::hot_cold_t hot_cold) noexcept(false); +void *operator new(size_t size, const std::nothrow_t &, + malloc_namespace::hot_cold_t hot_cold) noexcept; +void *operator new[](size_t size, const std::nothrow_t &, + malloc_namespace::hot_cold_t hot_cold) noexcept; +void *operator new(size_t size, std::align_val_t alignment, + malloc_namespace::hot_cold_t hot_cold) noexcept(false); +void *operator new[](size_t size, std::align_val_t alignment, + malloc_namespace::hot_cold_t hot_cold) noexcept(false); +void *operator new(size_t size, std::align_val_t alignment, + const std::nothrow_t &, + malloc_namespace::hot_cold_t hot_cold) noexcept; +void *operator new[](size_t size, std::align_val_t alignment, + const std::nothrow_t &, + malloc_namespace::hot_cold_t hot_cold) noexcept; + +// All explicit operator new calls should not get any builtin attribute, whereas +// all implicit new expressions should get builtin attributes. All of the +// declarations should get nobuiltin attributes. + +void hot_cold_new() { + // CHECK: call noalias noundef nonnull ptr @_Znwm12__hot_cold_t(i64 noundef 1, i8 noundef zeroext 0) [[ATTR_NO_BUILTIN_CALL:#[^ ]*]] + operator new(1, (__hot_cold_t)0); + // CHECK: call noalias noundef nonnull ptr @_Znwm12__hot_cold_t(i64 noundef 4, i8 noundef zeroext 0) [[ATTR_BUILTIN_CALL:#[^ ]*]] + new ((__hot_cold_t)0) int; +} + +// CHECK: declare noundef nonnull ptr @_Znwm12__hot_cold_t(i64 noundef, i8 noundef zeroext) [[ATTR_NOBUILTIN:#[^ ]*]] + +void hot_cold_new_array() { + // CHECK: call noalias noundef nonnull ptr @_Znam12__hot_cold_t(i64 noundef 1, i8 noundef zeroext 0) [[ATTR_NO_BUILTIN_CALL:#[^ ]*]] + operator new[](1, (__hot_cold_t)0); + // CHECK: call noalias noundef nonnull ptr @_Znam12__hot_cold_t(i64 noundef 4, i8 noundef zeroext 0) [[ATTR_BUILTIN_CALL:#[^ ]*]] + new ((__hot_cold_t)0) int[1]; +} + +// CHECK: declare noundef nonnull ptr @_Znam12__hot_cold_t(i64 noundef, i8 noundef zeroext) [[ATTR_NOBUILTIN:#[^ ]*]] + +void hot_cold_new_nothrow() { + // CHECK: call noalias noundef ptr @_ZnwmRKSt9nothrow_t12__hot_cold_t(i64 noundef 1, {{.*}} @nothrow, i8 noundef zeroext 0) [[ATTR_NO_BUILTIN_NOTHROW_CALL:#[^ ]*]] + operator new(1, nothrow, (__hot_cold_t)0); + // CHECK: call noalias noundef ptr @_ZnwmRKSt9nothrow_t12__hot_cold_t(i64 noundef 4, {{.*}} @nothrow, i8 noundef zeroext 0) [[ATTR_BUILTIN_NOTHROW_CALL:#[^ ]*]] + new (nothrow, (__hot_cold_t)0) int; +} + +// CHECK: declare noundef ptr @_ZnwmRKSt9nothrow_t12__hot_cold_t(i64 noundef, ptr noundef nonnull align 1 dereferenceable(1), i8 noundef zeroext) [[ATTR_NOBUILTIN_NOTHROW:#[^ ]*]] + +void hot_cold_new_nothrow_array() { + // CHECK: call noalias noundef ptr @_ZnamRKSt9nothrow_t12__hot_cold_t(i64 noundef 1, {{.*}} @nothrow, i8 noundef zeroext 0) [[ATTR_NO_BUILTIN_NOTHROW_CALL:#[^ ]*]] + operator new[](1, nothrow, (__hot_cold_t)0); + // CHECK: call noalias noundef ptr @_ZnamRKSt9nothrow_t12__hot_cold_t(i64 noundef 4, {{.*}} @nothrow, i8 noundef zeroext 0) [[ATTR_BUILTIN_NOTHROW_CALL:#[^ ]*]] + new (nothrow, (__hot_cold_t)0) int[1]; +} + +// CHECK: declare noundef ptr @_ZnamRKSt9nothrow_t12__hot_cold_t(i64 noundef, ptr noundef nonnull align 1 dereferenceable(1), i8 noundef zeroext) [[ATTR_NOBUILTIN_NOTHROW:#[^ ]*]] + +class alignas(32) alignedstruct { + int x; +}; + +void hot_cold_new_align() { + // CHECK: call noalias noundef nonnull align 32 ptr @_ZnwmSt11align_val_t12__hot_cold_t(i64 noundef 1, i64 noundef 32, i8 noundef zeroext 0) [[ATTR_NO_BUILTIN_CALL:#[^ ]*]] + operator new(1, (std::align_val_t)32, (__hot_cold_t)0); + // CHECK: call noalias noundef nonnull align 32 ptr @_ZnwmSt11align_val_t12__hot_cold_t(i64 noundef 32, i64 noundef 32, i8 noundef zeroext 0) [[ATTR_BUILTIN_CALL:#[^ ]*]] + new ((__hot_cold_t)0) alignedstruct; +} + +// CHECK: declare noundef nonnull ptr @_ZnwmSt11align_val_t12__hot_cold_t(i64 noundef, i64 noundef, i8 noundef zeroext) [[ATTR_NOBUILTIN:#[^ ]*]] + +void hot_cold_new_align_array() { + // CHECK: call noalias noundef nonnull align 32 ptr @_ZnamSt11align_val_t12__hot_cold_t(i64 noundef 1, i64 noundef 32, i8 noundef zeroext 0) [[ATTR_NO_BUILTIN_CALL:#[^ ]*]] + operator new[](1, (std::align_val_t)32, (__hot_cold_t)0); + // CHECK: call noalias noundef nonnull align 32 ptr @_ZnamSt11align_val_t12__hot_cold_t(i64 noundef 32, i64 noundef 32, i8 noundef zeroext 0) [[ATTR_BUILTIN_CALL:#[^ ]*]] + new ((__hot_cold_t)0) alignedstruct[1]; +} + +// CHECK: declare noundef nonnull ptr @_ZnamSt11align_val_t12__hot_cold_t(i64 noundef, i64 noundef, i8 noundef zeroext) [[ATTR_NOBUILTIN:#[^ ]*]] + +void hot_cold_new_align_nothrow() { + // CHECK: call noalias noundef align 32 ptr @_ZnwmSt11align_val_tRKSt9nothrow_t12__hot_cold_t(i64 noundef 1, i64 noundef 32, {{.*}} @nothrow, i8 noundef zeroext 0) [[ATTR_NO_BUILTIN_NOTHROW_CALL:#[^ ]*]] + operator new(1, (std::align_val_t)32, nothrow, (__hot_cold_t)0); + // CHECK: call noalias noundef align 32 ptr @_ZnwmSt11align_val_tRKSt9nothrow_t12__hot_cold_t(i64 noundef 32, i64 noundef 32, {{.*}} @nothrow, i8 noundef zeroext 0) [[ATTR_BUILTIN_NOTHROW_CALL:#[^ ]*]] + new (nothrow, (__hot_cold_t)0) alignedstruct; +} + +// CHECK: declare noundef ptr @_ZnwmSt11align_val_tRKSt9nothrow_t12__hot_cold_t(i64 noundef, i64 noundef, ptr noundef nonnull align 1 dereferenceable(1), i8 noundef zeroext) [[ATTR_NOBUILTIN_NOTHROW:#[^ ]*]] + +void hot_cold_new_align_nothrow_array() { + // CHECK: call noalias noundef align 32 ptr @_ZnamSt11align_val_tRKSt9nothrow_t12__hot_cold_t(i64 noundef 1, i64 noundef 32, {{.*}} @nothrow, i8 noundef zeroext 0) [[ATTR_NO_BUILTIN_NOTHROW_CALL:#[^ ]*]] + operator new[](1, (std::align_val_t)32, nothrow, (__hot_cold_t)0); + // CHECK: call noalias noundef align 32 ptr @_ZnamSt11align_val_tRKSt9nothrow_t12__hot_cold_t(i64 noundef 32, i64 noundef 32, {{.*}} @nothrow, i8 noundef zeroext 0) [[ATTR_BUILTIN_NOTHROW_CALL:#[^ ]*]] + new (nothrow, (__hot_cold_t)0) alignedstruct[1]; +} + +// CHECK: declare noundef ptr @_ZnamSt11align_val_tRKSt9nothrow_t12__hot_cold_t(i64 noundef, i64 noundef, ptr noundef nonnull align 1 dereferenceable(1), i8 noundef zeroext) [[ATTR_NOBUILTIN_NOTHROW:#[^ ]*]] + +// CHECK-DAG: attributes [[ATTR_NOBUILTIN]] = { nobuiltin allocsize(0) {{.*}} } +// CHECK-DAG: attributes [[ATTR_NOBUILTIN_NOTHROW]] = { nobuiltin nounwind allocsize(0) {{.*}} } +// CHECK-DAG: attributes [[ATTR_NO_BUILTIN_CALL]] = { allocsize(0) } +// CHECK-DAG: attributes [[ATTR_BUILTIN_CALL]] = { builtin allocsize(0) } +// CHECK-DAG: attributes [[ATTR_NO_BUILTIN_NOTHROW_CALL]] = { nounwind allocsize(0) } +// CHECK-DAG: attributes [[ATTR_BUILTIN_NOTHROW_CALL]] = { builtin nounwind allocsize(0) }