[clang] 2cc0c0d - [MemProf] Recognize hot/cold operator new as replaceable allocations

Teresa Johnson via cfe-commits cfe-commits at lists.llvm.org
Mon May 1 13:37:51 PDT 2023


Author: Teresa Johnson
Date: 2023-05-01T13:37:40-07:00
New Revision: 2cc0c0de802178dc7e5408497e2ec53b6c9728fa

URL: https://github.com/llvm/llvm-project/commit/2cc0c0de802178dc7e5408497e2ec53b6c9728fa
DIFF: https://github.com/llvm/llvm-project/commit/2cc0c0de802178dc7e5408497e2ec53b6c9728fa.diff

LOG: [MemProf] Recognize hot/cold operator new as replaceable allocations

Follow up to LLVM-side change to allow conversion to hot/cold hinted
operator new, not yet standard but supported by open source tcmalloc.
The LLVM change in a35206d78280e0ebcf48cd21bc5fff5c3b9c73fa added the
necessary support to recognize these as library functions, and we
subsequently found that a clang-side change is needed to give them
builtin/nobuiltin attributes as appropriate so they have consistent
removeability.

Based on discussion with Richard Smith, converted the parameter type to
a reserved identifier (39f7b48671dae5fbe3afc49f33f50c2b6340bb89) and
added support in this patch to recognize it in clang.

Differential Revision: https://reviews.llvm.org/D149600

Added: 
    clang/test/CodeGenCXX/new_hot_cold.cpp

Modified: 
    clang/lib/AST/Decl.cpp

Removed: 
    


################################################################################
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<FunctionProtoType>();
-  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<TypedefType>())
+      T = TD->getDecl()->getUnderlyingType();
+    IdentifierInfo *II = T->getAs<EnumType>()->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) }


        


More information about the cfe-commits mailing list