[llvm] [MemProf] Allow hint update on existing calls to nobuiltin hot/cold new (PR #156476)

Teresa Johnson via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 2 08:24:21 PDT 2025


https://github.com/teresajohnson created https://github.com/llvm/llvm-project/pull/156476

Explicit calls to ::operator new are marked nobuiltin and cannot be
elided or updated as they may call user defined versions. However,
existing calls to the hot/cold versions of new only need their hint
parameter value updated, which does not mutate the call.


>From b237766c63f5b6a29ffcb6306b8d5756969aeb51 Mon Sep 17 00:00:00 2001
From: Teresa Johnson <tejohnson at google.com>
Date: Tue, 2 Sep 2025 08:19:24 -0700
Subject: [PATCH] [MemProf] Allow hint update on existing calls to nobuiltin
 hot/cold new

Explicit calls to ::operator new are marked nobuiltin and cannot be
elided or updated as they may call user defined versions. However,
existing calls to the hot/cold versions of new only need their hint
parameter value updated, which does not mutate the call.
---
 .../llvm/Transforms/Utils/SimplifyLibCalls.h  |  1 +
 .../lib/Transforms/Utils/SimplifyLibCalls.cpp | 38 +++++++-
 .../InstCombine/simplify-libcalls-new.ll      | 86 +++++++++++++++++++
 3 files changed, 123 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
index 128502b99d9a3..deb3d6c44ef09 100644
--- a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
+++ b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
@@ -189,6 +189,7 @@ class LibCallSimplifier {
   Value *optimizeMemSet(CallInst *CI, IRBuilderBase &B);
   Value *optimizeRealloc(CallInst *CI, IRBuilderBase &B);
   Value *optimizeNew(CallInst *CI, IRBuilderBase &B, LibFunc &Func);
+  Value *optimizeExistingHotColdNew(CallInst *CI, IRBuilderBase &B);
   Value *optimizeWcslen(CallInst *CI, IRBuilderBase &B);
   Value *optimizeBCopy(CallInst *CI, IRBuilderBase &B);
 
diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index 2d6a748f45079..821cd5d0b831a 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -1719,6 +1719,37 @@ Value *LibCallSimplifier::optimizeRealloc(CallInst *CI, IRBuilderBase &B) {
   return nullptr;
 }
 
+// Allow existing calls to operator new() that takes a __hot_cold_t parameter to
+// be updated with a compiler-determined hot cold hint value. This is used in
+// cases where the call is marked nobuiltin (because operator new called
+// explicitly) and therefore cannot be replaced with a different callee.
+Value *LibCallSimplifier::optimizeExistingHotColdNew(CallInst *CI,
+                                                     IRBuilderBase &B) {
+  if (!OptimizeHotColdNew || !OptimizeExistingHotColdNew)
+    return nullptr;
+  Function *Callee = CI->getCalledFunction();
+  if (!Callee)
+    return nullptr;
+  LibFunc Func;
+  if (!TLI->getLibFunc(*Callee, Func))
+    return nullptr;
+  switch (Func) {
+  case LibFunc_Znwm12__hot_cold_t:
+  case LibFunc_ZnwmRKSt9nothrow_t12__hot_cold_t:
+  case LibFunc_ZnwmSt11align_val_t12__hot_cold_t:
+  case LibFunc_ZnwmSt11align_val_tRKSt9nothrow_t12__hot_cold_t:
+  case LibFunc_Znam12__hot_cold_t:
+  case LibFunc_ZnamRKSt9nothrow_t12__hot_cold_t:
+  case LibFunc_ZnamSt11align_val_t12__hot_cold_t:
+  case LibFunc_ZnamSt11align_val_tRKSt9nothrow_t12__hot_cold_t:
+  case LibFunc_size_returning_new_hot_cold:
+  case LibFunc_size_returning_new_aligned_hot_cold:
+    return optimizeNew(CI, B, Func);
+  default:
+    return nullptr;
+  }
+}
+
 // When enabled, replace operator new() calls marked with a hot or cold memprof
 // attribute with an operator new() call that takes a __hot_cold_t parameter.
 // Currently this is supported by the open source version of tcmalloc, see:
@@ -4094,8 +4125,11 @@ Value *LibCallSimplifier::optimizeCall(CallInst *CI, IRBuilderBase &Builder) {
   // TODO: Split out the code below that operates on FP calls so that
   //       we can all non-FP calls with the StrictFP attribute to be
   //       optimized.
-  if (CI->isNoBuiltin())
-    return nullptr;
+  if (CI->isNoBuiltin()) {
+    // If this is an existing call to a hot cold operator new, we can update the
+    // hint parameter value, which doesn't change the callee.
+    return optimizeExistingHotColdNew(CI, Builder);
+  }
 
   LibFunc Func;
   Function *Callee = CI->getCalledFunction();
diff --git a/llvm/test/Transforms/InstCombine/simplify-libcalls-new.ll b/llvm/test/Transforms/InstCombine/simplify-libcalls-new.ll
index 83471f1d0f613..1cf095020f050 100644
--- a/llvm/test/Transforms/InstCombine/simplify-libcalls-new.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-libcalls-new.ll
@@ -36,6 +36,10 @@ define void @new() {
   ; HOTCOLD: @_Znwm12__hot_cold_t(i64 10, i8 [[HOT]])
   %call2 = call ptr @_Znwm(i64 10) #2
   call void @dummy(ptr %call2)
+  ;; Attribute cold on a nobuiltin call has no effect.
+  ; HOTCOLD: @_Znwm(i64 10)
+  %call3 = call ptr @_Znwm(i64 10) #6
+  call void @dummy(ptr %call3)
   ret void
 }
 
@@ -56,6 +60,10 @@ define void @new_align() {
   ; HOTCOLD: @_ZnwmSt11align_val_t12__hot_cold_t(i64 10, i64 8, i8 [[HOT]])
   %call2 = call ptr @_ZnwmSt11align_val_t(i64 10, i64 8) #2
   call void @dummy(ptr %call2)
+  ;; Attribute cold on a nobuiltin call has no effect.
+  ; HOTCOLD: @_ZnwmSt11align_val_t(i64 10, i64 8)
+  %call3 = call ptr @_ZnwmSt11align_val_t(i64 10, i64 8) #6
+  call void @dummy(ptr %call3)
   ret void
 }
 
@@ -77,6 +85,10 @@ define void @new_nothrow() {
   ; HOTCOLD: @_ZnwmRKSt9nothrow_t12__hot_cold_t(i64 10, ptr nonnull %nt, i8 [[HOT]])
   %call2 = call ptr @_ZnwmRKSt9nothrow_t(i64 10, ptr %nt) #2
   call void @dummy(ptr %call2)
+  ;; Attribute cold on a nobuiltin call has no effect.
+  ; HOTCOLD: @_ZnwmRKSt9nothrow_t(i64 10, ptr nonnull %nt)
+  %call3 = call ptr @_ZnwmRKSt9nothrow_t(i64 10, ptr %nt) #6
+  call void @dummy(ptr %call3)
   ret void
 }
 
@@ -99,6 +111,10 @@ define void @new_align_nothrow() {
   ; HOTCOLD: @_ZnwmSt11align_val_tRKSt9nothrow_t12__hot_cold_t(i64 10, i64 8, ptr nonnull %nt, i8 [[HOT]])
   %call2 = call ptr @_ZnwmSt11align_val_tRKSt9nothrow_t(i64 10, i64 8, ptr %nt) #2
   call void @dummy(ptr %call2)
+  ;; Attribute cold on a nobuiltin call has no effect.
+  ; HOTCOLD: @_ZnwmSt11align_val_tRKSt9nothrow_t(i64 10, i64 8, ptr nonnull %nt)
+  %call3 = call ptr @_ZnwmSt11align_val_tRKSt9nothrow_t(i64 10, i64 8, ptr %nt) #6
+  call void @dummy(ptr %call3)
   ret void
 }
 
@@ -118,6 +134,10 @@ define void @array_new() {
   ; HOTCOLD: @_Znam12__hot_cold_t(i64 10, i8 [[HOT]])
   %call2 = call ptr @_Znam(i64 10) #2
   call void @dummy(ptr %call2)
+  ;; Attribute cold on a nobuiltin call has no effect.
+  ; HOTCOLD: @_Znam(i64 10)
+  %call3 = call ptr @_Znam(i64 10) #6
+  call void @dummy(ptr %call3)
   ret void
 }
 
@@ -138,6 +158,10 @@ define void @array_new_align() {
   ; HOTCOLD: @_ZnamSt11align_val_t12__hot_cold_t(i64 10, i64 8, i8 [[HOT]])
   %call2 = call ptr @_ZnamSt11align_val_t(i64 10, i64 8) #2
   call void @dummy(ptr %call2)
+  ;; Attribute cold on a nobuiltin call has no effect.
+  ; HOTCOLD: @_ZnamSt11align_val_t(i64 10, i64 8)
+  %call3 = call ptr @_ZnamSt11align_val_t(i64 10, i64 8) #6
+  call void @dummy(ptr %call3)
   ret void
 }
 
@@ -159,6 +183,10 @@ define void @array_new_nothrow() {
   ; HOTCOLD: @_ZnamRKSt9nothrow_t12__hot_cold_t(i64 10, ptr nonnull %nt, i8 [[HOT]])
   %call2 = call ptr @_ZnamRKSt9nothrow_t(i64 10, ptr %nt) #2
   call void @dummy(ptr %call2)
+  ;; Attribute cold on a nobuiltin call has no effect.
+  ; HOTCOLD: @_ZnamRKSt9nothrow_t(i64 10, ptr nonnull %nt)
+  %call3 = call ptr @_ZnamRKSt9nothrow_t(i64 10, ptr %nt) #6
+  call void @dummy(ptr %call3)
   ret void
 }
 
@@ -181,6 +209,10 @@ define void @array_new_align_nothrow() {
   ; HOTCOLD: @_ZnamSt11align_val_tRKSt9nothrow_t12__hot_cold_t(i64 10, i64 8, ptr nonnull %nt, i8 [[HOT]])
   %call2 = call ptr @_ZnamSt11align_val_tRKSt9nothrow_t(i64 10, i64 8, ptr %nt) #2
   call void @dummy(ptr %call2)
+  ;; Attribute cold on a nobuiltin call has no effect.
+  ; HOTCOLD: @_ZnamSt11align_val_tRKSt9nothrow_t(i64 10, i64 8, ptr nonnull %nt)
+  %call3 = call ptr @_ZnamSt11align_val_tRKSt9nothrow_t(i64 10, i64 8, ptr %nt) #6
+  call void @dummy(ptr %call3)
   ret void
 }
 
@@ -200,6 +232,10 @@ define void @new_hot_cold() {
   ; HOTCOLD: @_Znwm12__hot_cold_t(i64 10, i8 [[PREVHINTHOT]])
   %call2 = call ptr @_Znwm12__hot_cold_t(i64 10, i8 7) #2
   call void @dummy(ptr %call2)
+  ;; Attribute cold on a nobuiltin existing hot/cold call updates the hint.
+  ; HOTCOLD: @_Znwm12__hot_cold_t(i64 10, i8 [[PREVHINTCOLD]])
+  %call3 = call ptr @_Znwm12__hot_cold_t(i64 10, i8 7) #6
+  call void @dummy(ptr %call3)
   ret void
 }
 
@@ -219,6 +255,10 @@ define void @new_align_hot_cold() {
   ; HOTCOLD: @_ZnwmSt11align_val_t12__hot_cold_t(i64 10, i64 8, i8 [[PREVHINTHOT]])
   %call2 = call ptr @_ZnwmSt11align_val_t12__hot_cold_t(i64 10, i64 8, i8 7) #2
   call void @dummy(ptr %call2)
+  ;; Attribute cold on a nobuiltin existing hot/cold call updates the hint.
+  ; HOTCOLD: @_ZnwmSt11align_val_t12__hot_cold_t(i64 10, i64 8, i8 [[PREVHINTCOLD]])
+  %call3 = call ptr @_ZnwmSt11align_val_t12__hot_cold_t(i64 10, i64 8, i8 7) #6
+  call void @dummy(ptr %call3)
   ret void
 }
 
@@ -239,6 +279,10 @@ define void @new_nothrow_hot_cold() {
   ; HOTCOLD: @_ZnwmRKSt9nothrow_t12__hot_cold_t(i64 10, ptr nonnull %nt, i8 [[PREVHINTHOT]])
   %call2 = call ptr @_ZnwmRKSt9nothrow_t12__hot_cold_t(i64 10, ptr %nt, i8 7) #2
   call void @dummy(ptr %call2)
+  ;; Attribute cold on a nobuiltin existing hot/cold call updates the hint.
+  ; HOTCOLD: @_ZnwmRKSt9nothrow_t12__hot_cold_t(i64 10, ptr nonnull %nt, i8 [[PREVHINTCOLD]])
+  %call3 = call ptr @_ZnwmRKSt9nothrow_t12__hot_cold_t(i64 10, ptr %nt, i8 7) #6
+  call void @dummy(ptr %call3)
   ret void
 }
 
@@ -259,6 +303,10 @@ define void @new_align_nothrow_hot_cold() {
   ; HOTCOLD: @_ZnwmSt11align_val_tRKSt9nothrow_t12__hot_cold_t(i64 10, i64 8, ptr nonnull %nt, i8 [[PREVHINTHOT]])
   %call2 = call ptr @_ZnwmSt11align_val_tRKSt9nothrow_t12__hot_cold_t(i64 10, i64 8, ptr %nt, i8 7) #2
   call void @dummy(ptr %call2)
+  ;; Attribute cold on a nobuiltin existing hot/cold call updates the hint.
+  ; HOTCOLD: @_ZnwmSt11align_val_tRKSt9nothrow_t12__hot_cold_t(i64 10, i64 8, ptr nonnull %nt, i8 [[PREVHINTCOLD]])
+  %call3 = call ptr @_ZnwmSt11align_val_tRKSt9nothrow_t12__hot_cold_t(i64 10, i64 8, ptr %nt, i8 7) #6
+  call void @dummy(ptr %call3)
   ret void
 }
 
@@ -278,6 +326,10 @@ define void @array_new_hot_cold() {
   ; HOTCOLD: @_Znam12__hot_cold_t(i64 10, i8 [[PREVHINTHOT]])
   %call2 = call ptr @_Znam12__hot_cold_t(i64 10, i8 7) #2
   call void @dummy(ptr %call2)
+  ;; Attribute cold on a nobuiltin existing hot/cold call updates the hint.
+  ; HOTCOLD: @_Znam12__hot_cold_t(i64 10, i8 [[PREVHINTCOLD]])
+  %call3 = call ptr @_Znam12__hot_cold_t(i64 10, i8 7) #6
+  call void @dummy(ptr %call3)
   ret void
 }
 
@@ -297,6 +349,10 @@ define void @array_new_align_hot_cold() {
   ; HOTCOLD: @_ZnamSt11align_val_t12__hot_cold_t(i64 10, i64 8, i8 [[PREVHINTHOT]])
   %call2 = call ptr @_ZnamSt11align_val_t12__hot_cold_t(i64 10, i64 8, i8 7) #2
   call void @dummy(ptr %call2)
+  ;; Attribute cold on a nobuiltin existing hot/cold call updates the hint.
+  ; HOTCOLD: @_ZnamSt11align_val_t12__hot_cold_t(i64 10, i64 8, i8 [[PREVHINTCOLD]])
+  %call3 = call ptr @_ZnamSt11align_val_t12__hot_cold_t(i64 10, i64 8, i8 7) #6
+  call void @dummy(ptr %call3)
   ret void
 }
 
@@ -317,6 +373,10 @@ define void @array_new_nothrow_hot_cold() {
   ; HOTCOLD: @_ZnamRKSt9nothrow_t12__hot_cold_t(i64 10, ptr nonnull %nt, i8 [[PREVHINTHOT]])
   %call2 = call ptr @_ZnamRKSt9nothrow_t12__hot_cold_t(i64 10, ptr %nt, i8 7) #2
   call void @dummy(ptr %call2)
+  ;; Attribute cold on a nobuiltin existing hot/cold call updates the hint.
+  ; HOTCOLD: @_ZnamRKSt9nothrow_t12__hot_cold_t(i64 10, ptr nonnull %nt, i8 [[PREVHINTCOLD]])
+  %call3 = call ptr @_ZnamRKSt9nothrow_t12__hot_cold_t(i64 10, ptr %nt, i8 7) #6
+  call void @dummy(ptr %call3)
   ret void
 }
 
@@ -337,6 +397,10 @@ define void @array_new_align_nothrow_hot_cold() {
   ; HOTCOLD: @_ZnamSt11align_val_tRKSt9nothrow_t12__hot_cold_t(i64 10, i64 8, ptr nonnull %nt, i8 [[PREVHINTHOT]])
   %call2 = call ptr @_ZnamSt11align_val_tRKSt9nothrow_t12__hot_cold_t(i64 10, i64 8, ptr %nt, i8 7) #2
   call void @dummy(ptr %call2)
+  ;; Attribute cold on a nobuiltin existing hot/cold call updates the hint.
+  ; HOTCOLD: @_ZnamSt11align_val_tRKSt9nothrow_t12__hot_cold_t(i64 10, i64 8, ptr nonnull %nt, i8 [[PREVHINTCOLD]])
+  %call3 = call ptr @_ZnamSt11align_val_tRKSt9nothrow_t12__hot_cold_t(i64 10, i64 8, ptr %nt, i8 7) #6
+  call void @dummy(ptr %call3)
   ret void
 }
 
@@ -359,6 +423,11 @@ define void @size_returning_test() {
   %call2 = call {ptr, i64} @__size_returning_new(i64 10) #5
   %p2  = extractvalue {ptr, i64} %call2, 0
   call void @dummy(ptr %p2)
+  ;; Attribute cold on a nobuiltin call has no effect.
+  ; HOTCOLD: @__size_returning_new(i64 10)
+  %call3 = call {ptr, i64} @__size_returning_new(i64 10) #6
+  %p3 = extractvalue {ptr, i64} %call3, 0
+  call void @dummy(ptr %p3)
   ret void
 }
 
@@ -381,6 +450,11 @@ define void @size_returning_aligned_test() {
   %call2 = call {ptr, i64} @__size_returning_new_aligned(i64 10, i64 8) #5
   %p2  = extractvalue {ptr, i64} %call2, 0
   call void @dummy(ptr %p2)
+  ;; Attribute cold on a nobuiltin call has no effect.
+  ; HOTCOLD: @__size_returning_new_aligned(i64 10, i64 8)
+  %call3 = call {ptr, i64} @__size_returning_new_aligned(i64 10, i64 8) #6
+  %p3 = extractvalue {ptr, i64} %call3, 0
+  call void @dummy(ptr %p3)
   ret void
 }
 
@@ -403,6 +477,11 @@ define void @size_returning_update_test() {
   %call2 = call {ptr, i64} @__size_returning_new_hot_cold(i64 10, i8 7) #5
   %p2 = extractvalue {ptr, i64} %call2, 0
   call void @dummy(ptr %p2)
+  ;; Attribute cold on a nobuiltin existing hot/cold call updates the hint.
+  ; HOTCOLD: @__size_returning_new_hot_cold(i64 10, i8 [[PREVHINTCOLD]])
+  %call3 = call {ptr, i64} @__size_returning_new_hot_cold(i64 10, i8 7) #6
+  %p3 = extractvalue {ptr, i64} %call3, 0
+  call void @dummy(ptr %p3)
   ret void
 }
 
@@ -425,6 +504,11 @@ define void @size_returning_aligned_update_test() {
   %call2 = call {ptr, i64} @__size_returning_new_aligned_hot_cold(i64 10, i64 8, i8 7) #5
   %p2 = extractvalue {ptr, i64} %call2, 0
   call void @dummy(ptr %p2)
+  ;; Attribute cold on a nobuiltin existing hot/cold call updates the hint.
+  ; HOTCOLD: @__size_returning_new_aligned_hot_cold(i64 10, i64 8, i8 [[PREVHINTCOLD]])
+  %call3 = call {ptr, i64} @__size_returning_new_aligned_hot_cold(i64 10, i64 8, i8 7) #6
+  %p3 = extractvalue {ptr, i64} %call3, 0
+  call void @dummy(ptr %p3)
   ret void
 }
 
@@ -463,3 +547,5 @@ attributes #2 = { builtin allocsize(0) "memprof"="hot" }
 attributes #3 = { "memprof" = "cold" }
 attributes #4 = { "memprof" = "notcold" }
 attributes #5 = { "memprof" = "hot" }
+
+attributes #6 = { nobuiltin allocsize(0) "memprof"="cold" }



More information about the llvm-commits mailing list