[llvm] [MemProf] Support cold-only and min hint when optimizing existing hot/cold new (PR #218803)
Teresa Johnson via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 25 19:52:21 PDT 2026
https://github.com/teresajohnson updated https://github.com/llvm/llvm-project/pull/218803
>From f517aa2396478669ab658b98e0f20b30adb65141 Mon Sep 17 00:00:00 2001
From: Teresa Johnson <tejohnson at google.com>
Date: Tue, 25 Aug 2026 15:09:34 -0700
Subject: [PATCH 1/3] [MemProf] Support cold-only and min hint when optimizing
existing hot/cold new
Change -optimize-existing-hot-cold-new to an enum option supporting:
- none (default): Do not optimize existing hot/cold new calls
- cold: Only optimize existing hot/cold new calls if determined to be cold
- always: Always optimize existing hot/cold new calls
Add a new option -min-existing-hot-cold-new-hint (off by default) that,
when optimizing an existing hot/cold operator new call, takes the minimum
of the compiler hint and the existing hint. If the existing hint is a
compile-time constant, the minimum is computed at compile time; otherwise,
an llvm.umin intrinsic is emitted.
---
.../llvm/Transforms/Utils/BuildLibCalls.h | 13 +-
llvm/lib/Transforms/Utils/BuildLibCalls.cpp | 26 ++--
.../lib/Transforms/Utils/SimplifyLibCalls.cpp | 142 ++++++++++++------
.../InstCombine/simplify-libcalls-new.ll | 64 +++++++-
4 files changed, 174 insertions(+), 71 deletions(-)
diff --git a/llvm/include/llvm/Transforms/Utils/BuildLibCalls.h b/llvm/include/llvm/Transforms/Utils/BuildLibCalls.h
index f53e7d056ca9f..67b4ff985db63 100644
--- a/llvm/include/llvm/Transforms/Utils/BuildLibCalls.h
+++ b/llvm/include/llvm/Transforms/Utils/BuildLibCalls.h
@@ -284,27 +284,26 @@ namespace llvm {
/// Emit a call to the hot/cold operator new function.
LLVM_ABI Value *emitHotColdNew(Value *Num, IRBuilderBase &B,
const TargetLibraryInfo *TLI, LibFunc NewFunc,
- uint8_t HotCold);
+ Value *HotCold);
LLVM_ABI Value *emitHotColdNewNoThrow(Value *Num, Value *NoThrow,
IRBuilderBase &B,
const TargetLibraryInfo *TLI,
- LibFunc NewFunc, uint8_t HotCold);
+ LibFunc NewFunc, Value *HotCold);
LLVM_ABI Value *emitHotColdNewAligned(Value *Num, Value *Align,
IRBuilderBase &B,
const TargetLibraryInfo *TLI,
- LibFunc NewFunc, uint8_t HotCold);
+ LibFunc NewFunc, Value *HotCold);
LLVM_ABI Value *emitHotColdNewAlignedNoThrow(Value *Num, Value *Align,
Value *NoThrow, IRBuilderBase &B,
const TargetLibraryInfo *TLI,
- LibFunc NewFunc,
- uint8_t HotCold);
+ LibFunc NewFunc, Value *HotCold);
LLVM_ABI Value *emitHotColdSizeReturningNew(Value *Num, IRBuilderBase &B,
const TargetLibraryInfo *TLI,
- LibFunc NewFunc, uint8_t HotCold);
+ LibFunc NewFunc, Value *HotCold);
LLVM_ABI Value *
emitHotColdSizeReturningNewAligned(Value *Num, Value *Align, IRBuilderBase &B,
const TargetLibraryInfo *TLI,
- LibFunc NewFunc, uint8_t HotCold);
+ LibFunc NewFunc, Value *HotCold);
}
#endif
diff --git a/llvm/lib/Transforms/Utils/BuildLibCalls.cpp b/llvm/lib/Transforms/Utils/BuildLibCalls.cpp
index 0afcc9df40070..e9ef993e71ec1 100644
--- a/llvm/lib/Transforms/Utils/BuildLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/BuildLibCalls.cpp
@@ -2160,7 +2160,7 @@ Value *llvm::emitCalloc(Value *Num, Value *Size, IRBuilderBase &B,
Value *llvm::emitHotColdSizeReturningNew(Value *Num, IRBuilderBase &B,
const TargetLibraryInfo *TLI,
LibFunc SizeFeedbackNewFunc,
- uint8_t HotCold) {
+ Value *HotCold) {
Module *M = B.GetInsertBlock()->getModule();
if (!isLibFuncEmittable(M, TLI, SizeFeedbackNewFunc))
return nullptr;
@@ -2173,7 +2173,7 @@ Value *llvm::emitHotColdSizeReturningNew(Value *Num, IRBuilderBase &B,
FunctionCallee Func =
M->getOrInsertFunction(Name, SizedPtrT, Num->getType(), B.getInt8Ty());
inferNonMandatoryLibFuncAttrs(M, Name, *TLI);
- CallInst *CI = B.CreateCall(Func, {Num, B.getInt8(HotCold)}, "sized_ptr");
+ CallInst *CI = B.CreateCall(Func, {Num, HotCold}, "sized_ptr");
if (const Function *F = dyn_cast<Function>(Func.getCallee()))
CI->setCallingConv(F->getCallingConv());
@@ -2185,7 +2185,7 @@ Value *llvm::emitHotColdSizeReturningNewAligned(Value *Num, Value *Align,
IRBuilderBase &B,
const TargetLibraryInfo *TLI,
LibFunc SizeFeedbackNewFunc,
- uint8_t HotCold) {
+ Value *HotCold) {
Module *M = B.GetInsertBlock()->getModule();
if (!isLibFuncEmittable(M, TLI, SizeFeedbackNewFunc))
return nullptr;
@@ -2198,8 +2198,7 @@ Value *llvm::emitHotColdSizeReturningNewAligned(Value *Num, Value *Align,
FunctionCallee Func = M->getOrInsertFunction(Name, SizedPtrT, Num->getType(),
Align->getType(), B.getInt8Ty());
inferNonMandatoryLibFuncAttrs(M, Name, *TLI);
- CallInst *CI =
- B.CreateCall(Func, {Num, Align, B.getInt8(HotCold)}, "sized_ptr");
+ CallInst *CI = B.CreateCall(Func, {Num, Align, HotCold}, "sized_ptr");
if (const Function *F = dyn_cast<Function>(Func.getCallee()))
CI->setCallingConv(F->getCallingConv());
@@ -2209,7 +2208,7 @@ Value *llvm::emitHotColdSizeReturningNewAligned(Value *Num, Value *Align,
Value *llvm::emitHotColdNew(Value *Num, IRBuilderBase &B,
const TargetLibraryInfo *TLI, LibFunc NewFunc,
- uint8_t HotCold) {
+ Value *HotCold) {
Module *M = B.GetInsertBlock()->getModule();
if (!isLibFuncEmittable(M, TLI, NewFunc))
return nullptr;
@@ -2218,7 +2217,7 @@ Value *llvm::emitHotColdNew(Value *Num, IRBuilderBase &B,
FunctionCallee Func =
M->getOrInsertFunction(Name, B.getPtrTy(), Num->getType(), B.getInt8Ty());
inferNonMandatoryLibFuncAttrs(M, Name, *TLI);
- CallInst *CI = B.CreateCall(Func, {Num, B.getInt8(HotCold)}, Name);
+ CallInst *CI = B.CreateCall(Func, {Num, HotCold}, Name);
if (const Function *F =
dyn_cast<Function>(Func.getCallee()->stripPointerCasts()))
@@ -2229,7 +2228,7 @@ Value *llvm::emitHotColdNew(Value *Num, IRBuilderBase &B,
Value *llvm::emitHotColdNewNoThrow(Value *Num, Value *NoThrow, IRBuilderBase &B,
const TargetLibraryInfo *TLI,
- LibFunc NewFunc, uint8_t HotCold) {
+ LibFunc NewFunc, Value *HotCold) {
Module *M = B.GetInsertBlock()->getModule();
if (!isLibFuncEmittable(M, TLI, NewFunc))
return nullptr;
@@ -2238,7 +2237,7 @@ Value *llvm::emitHotColdNewNoThrow(Value *Num, Value *NoThrow, IRBuilderBase &B,
FunctionCallee Func = M->getOrInsertFunction(
Name, B.getPtrTy(), Num->getType(), NoThrow->getType(), B.getInt8Ty());
inferNonMandatoryLibFuncAttrs(M, Name, *TLI);
- CallInst *CI = B.CreateCall(Func, {Num, NoThrow, B.getInt8(HotCold)}, Name);
+ CallInst *CI = B.CreateCall(Func, {Num, NoThrow, HotCold}, Name);
if (const Function *F =
dyn_cast<Function>(Func.getCallee()->stripPointerCasts()))
@@ -2249,7 +2248,7 @@ Value *llvm::emitHotColdNewNoThrow(Value *Num, Value *NoThrow, IRBuilderBase &B,
Value *llvm::emitHotColdNewAligned(Value *Num, Value *Align, IRBuilderBase &B,
const TargetLibraryInfo *TLI,
- LibFunc NewFunc, uint8_t HotCold) {
+ LibFunc NewFunc, Value *HotCold) {
Module *M = B.GetInsertBlock()->getModule();
if (!isLibFuncEmittable(M, TLI, NewFunc))
return nullptr;
@@ -2258,7 +2257,7 @@ Value *llvm::emitHotColdNewAligned(Value *Num, Value *Align, IRBuilderBase &B,
FunctionCallee Func = M->getOrInsertFunction(
Name, B.getPtrTy(), Num->getType(), Align->getType(), B.getInt8Ty());
inferNonMandatoryLibFuncAttrs(M, Name, *TLI);
- CallInst *CI = B.CreateCall(Func, {Num, Align, B.getInt8(HotCold)}, Name);
+ CallInst *CI = B.CreateCall(Func, {Num, Align, HotCold}, Name);
if (const Function *F =
dyn_cast<Function>(Func.getCallee()->stripPointerCasts()))
@@ -2270,7 +2269,7 @@ Value *llvm::emitHotColdNewAligned(Value *Num, Value *Align, IRBuilderBase &B,
Value *llvm::emitHotColdNewAlignedNoThrow(Value *Num, Value *Align,
Value *NoThrow, IRBuilderBase &B,
const TargetLibraryInfo *TLI,
- LibFunc NewFunc, uint8_t HotCold) {
+ LibFunc NewFunc, Value *HotCold) {
Module *M = B.GetInsertBlock()->getModule();
if (!isLibFuncEmittable(M, TLI, NewFunc))
return nullptr;
@@ -2280,8 +2279,7 @@ Value *llvm::emitHotColdNewAlignedNoThrow(Value *Num, Value *Align,
Name, B.getPtrTy(), Num->getType(), Align->getType(), NoThrow->getType(),
B.getInt8Ty());
inferNonMandatoryLibFuncAttrs(M, Name, *TLI);
- CallInst *CI =
- B.CreateCall(Func, {Num, Align, NoThrow, B.getInt8(HotCold)}, Name);
+ CallInst *CI = B.CreateCall(Func, {Num, Align, NoThrow, HotCold}, Name);
if (const Function *F =
dyn_cast<Function>(Func.getCallee()->stripPointerCasts()))
diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index 44a3cbca85d0c..0275d01fed134 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -57,13 +57,36 @@ static cl::opt<bool>
static cl::opt<bool>
OptimizeHotColdNew("optimize-hot-cold-new", cl::Hidden, cl::init(false),
cl::desc("Enable hot/cold operator new library calls"));
-static cl::opt<bool> OptimizeExistingHotColdNew(
- "optimize-existing-hot-cold-new", cl::Hidden, cl::init(false),
+enum class OptimizeExistingHotColdNewKind {
+ None,
+ Cold,
+ Always,
+};
+static cl::opt<OptimizeExistingHotColdNewKind> OptimizeExistingHotColdNew(
+ "optimize-existing-hot-cold-new", cl::Hidden,
cl::desc(
- "Enable optimization of existing hot/cold operator new library calls"));
+ "Enable optimization of existing hot/cold operator new library calls"),
+ cl::values(
+ clEnumValN(
+ OptimizeExistingHotColdNewKind::None, "none",
+ "Do not optimize existing hot/cold operator new library calls"),
+ clEnumValN(OptimizeExistingHotColdNewKind::Cold, "cold",
+ "Only optimize existing hot/cold operator new library calls "
+ "if determined to be cold"),
+ clEnumValN(
+ OptimizeExistingHotColdNewKind::Always, "always",
+ "Always optimize existing hot/cold operator new library calls"),
+ clEnumValN(
+ OptimizeExistingHotColdNewKind::Always, "",
+ "Always optimize existing hot/cold operator new library calls")),
+ cl::init(OptimizeExistingHotColdNewKind::None), cl::ValueOptional);
static cl::opt<bool> OptimizeNoBuiltinHotColdNew(
"optimize-nobuiltin-hot-cold-new-new", cl::Hidden, cl::init(false),
cl::desc("Enable transformation of nobuiltin operator new library calls"));
+static cl::opt<bool> MinExistingHotColdNewHint(
+ "min-existing-hot-cold-new-hint", cl::Hidden, cl::init(false),
+ cl::desc("Take the minimum of compiler hint and existing hint when "
+ "optimizing existing hot/cold operator new library calls"));
namespace {
@@ -1777,7 +1800,7 @@ Value *LibCallSimplifier::maybeOptimizeNoBuiltinOperatorNew(CallInst *CI,
case LibFunc_size_returning_new_aligned_hot_cold:
// If the nobuiltin call already passes a hot_cold_t parameter, allow update
// of that parameter when enabled.
- if (!OptimizeExistingHotColdNew)
+ if (OptimizeExistingHotColdNew == OptimizeExistingHotColdNewKind::None)
return nullptr;
break;
default:
@@ -1796,10 +1819,12 @@ Value *LibCallSimplifier::optimizeNew(CallInst *CI, IRBuilderBase &B,
return nullptr;
uint8_t HotCold;
- if (CI->getAttributes().getFnAttr("memprof").getValueAsString() == "cold")
+ bool IsCold = false;
+ if (CI->getAttributes().getFnAttr("memprof").getValueAsString() == "cold") {
HotCold = ColdNewHintValue;
- else if (CI->getAttributes().getFnAttr("memprof").getValueAsString() ==
- "notcold")
+ IsCold = true;
+ } else if (CI->getAttributes().getFnAttr("memprof").getValueAsString() ==
+ "notcold")
HotCold = NotColdNewHintValue;
else if (CI->getAttributes().getFnAttr("memprof").getValueAsString() == "hot")
HotCold = HotNewHintValue;
@@ -1809,6 +1834,24 @@ Value *LibCallSimplifier::optimizeNew(CallInst *CI, IRBuilderBase &B,
else
return nullptr;
+ bool ShouldOptimizeExisting =
+ OptimizeExistingHotColdNew == OptimizeExistingHotColdNewKind::Always ||
+ (OptimizeExistingHotColdNew == OptimizeExistingHotColdNewKind::Cold &&
+ IsCold);
+
+ Value *HotColdVal = B.getInt8(HotCold);
+ auto getHotColdHintForExisting = [&](uint8_t HotCold) -> Value * {
+ if (!MinExistingHotColdNewHint)
+ return HotColdVal;
+ Value *ExistingHint = CI->getArgOperand(CI->arg_size() - 1);
+ if (auto *CIHint = dyn_cast<ConstantInt>(ExistingHint))
+ return B.getInt8(
+ std::min(HotCold, static_cast<uint8_t>(CIHint->getZExtValue())));
+ if (ExistingHint->getType() != B.getInt8Ty())
+ ExistingHint = B.CreateTruncOrBitCast(ExistingHint, B.getInt8Ty());
+ return B.CreateBinaryIntrinsic(Intrinsic::umin, ExistingHint, HotColdVal);
+ };
+
// For calls that already pass a hot/cold hint, only update the hint if
// directed by OptimizeExistingHotColdNew. For other calls to new, add a hint
// if cold or hot, and leave as-is for default handling if "notcold" aka warm.
@@ -1819,112 +1862,121 @@ Value *LibCallSimplifier::optimizeNew(CallInst *CI, IRBuilderBase &B,
Value *NewCall = nullptr;
switch (Func) {
case LibFunc_Znwm12__hot_cold_t:
- if (OptimizeExistingHotColdNew)
+ if (ShouldOptimizeExisting)
NewCall = emitHotColdNew(CI->getArgOperand(0), B, TLI,
- LibFunc_Znwm12__hot_cold_t, HotCold);
+ LibFunc_Znwm12__hot_cold_t,
+ getHotColdHintForExisting(HotCold));
break;
case LibFunc_Znwm:
NewCall = emitHotColdNew(CI->getArgOperand(0), B, TLI,
- LibFunc_Znwm12__hot_cold_t, HotCold);
+ LibFunc_Znwm12__hot_cold_t, HotColdVal);
break;
case LibFunc_Znam12__hot_cold_t:
- if (OptimizeExistingHotColdNew)
+ if (ShouldOptimizeExisting)
NewCall = emitHotColdNew(CI->getArgOperand(0), B, TLI,
- LibFunc_Znam12__hot_cold_t, HotCold);
+ LibFunc_Znam12__hot_cold_t,
+ getHotColdHintForExisting(HotCold));
break;
case LibFunc_Znam:
NewCall = emitHotColdNew(CI->getArgOperand(0), B, TLI,
- LibFunc_Znam12__hot_cold_t, HotCold);
+ LibFunc_Znam12__hot_cold_t, HotColdVal);
break;
case LibFunc_ZnwmRKSt9nothrow_t12__hot_cold_t:
- if (OptimizeExistingHotColdNew)
- NewCall = emitHotColdNewNoThrow(
- CI->getArgOperand(0), CI->getArgOperand(1), B, TLI,
- LibFunc_ZnwmRKSt9nothrow_t12__hot_cold_t, HotCold);
+ if (ShouldOptimizeExisting)
+ NewCall =
+ emitHotColdNewNoThrow(CI->getArgOperand(0), CI->getArgOperand(1), B,
+ TLI, LibFunc_ZnwmRKSt9nothrow_t12__hot_cold_t,
+ getHotColdHintForExisting(HotCold));
break;
case LibFunc_ZnwmRKSt9nothrow_t:
NewCall = emitHotColdNewNoThrow(
CI->getArgOperand(0), CI->getArgOperand(1), B, TLI,
- LibFunc_ZnwmRKSt9nothrow_t12__hot_cold_t, HotCold);
+ LibFunc_ZnwmRKSt9nothrow_t12__hot_cold_t, HotColdVal);
break;
case LibFunc_ZnamRKSt9nothrow_t12__hot_cold_t:
- if (OptimizeExistingHotColdNew)
- NewCall = emitHotColdNewNoThrow(
- CI->getArgOperand(0), CI->getArgOperand(1), B, TLI,
- LibFunc_ZnamRKSt9nothrow_t12__hot_cold_t, HotCold);
+ if (ShouldOptimizeExisting)
+ NewCall =
+ emitHotColdNewNoThrow(CI->getArgOperand(0), CI->getArgOperand(1), B,
+ TLI, LibFunc_ZnamRKSt9nothrow_t12__hot_cold_t,
+ getHotColdHintForExisting(HotCold));
break;
case LibFunc_ZnamRKSt9nothrow_t:
NewCall = emitHotColdNewNoThrow(
CI->getArgOperand(0), CI->getArgOperand(1), B, TLI,
- LibFunc_ZnamRKSt9nothrow_t12__hot_cold_t, HotCold);
+ LibFunc_ZnamRKSt9nothrow_t12__hot_cold_t, HotColdVal);
break;
case LibFunc_ZnwmSt11align_val_t12__hot_cold_t:
- if (OptimizeExistingHotColdNew)
- NewCall = emitHotColdNewAligned(
- CI->getArgOperand(0), CI->getArgOperand(1), B, TLI,
- LibFunc_ZnwmSt11align_val_t12__hot_cold_t, HotCold);
+ if (ShouldOptimizeExisting)
+ NewCall =
+ emitHotColdNewAligned(CI->getArgOperand(0), CI->getArgOperand(1), B,
+ TLI, LibFunc_ZnwmSt11align_val_t12__hot_cold_t,
+ getHotColdHintForExisting(HotCold));
break;
case LibFunc_ZnwmSt11align_val_t:
NewCall = emitHotColdNewAligned(
CI->getArgOperand(0), CI->getArgOperand(1), B, TLI,
- LibFunc_ZnwmSt11align_val_t12__hot_cold_t, HotCold);
+ LibFunc_ZnwmSt11align_val_t12__hot_cold_t, HotColdVal);
break;
case LibFunc_ZnamSt11align_val_t12__hot_cold_t:
- if (OptimizeExistingHotColdNew)
- NewCall = emitHotColdNewAligned(
- CI->getArgOperand(0), CI->getArgOperand(1), B, TLI,
- LibFunc_ZnamSt11align_val_t12__hot_cold_t, HotCold);
+ if (ShouldOptimizeExisting)
+ NewCall =
+ emitHotColdNewAligned(CI->getArgOperand(0), CI->getArgOperand(1), B,
+ TLI, LibFunc_ZnamSt11align_val_t12__hot_cold_t,
+ getHotColdHintForExisting(HotCold));
break;
case LibFunc_ZnamSt11align_val_t:
NewCall = emitHotColdNewAligned(
CI->getArgOperand(0), CI->getArgOperand(1), B, TLI,
- LibFunc_ZnamSt11align_val_t12__hot_cold_t, HotCold);
+ LibFunc_ZnamSt11align_val_t12__hot_cold_t, HotColdVal);
break;
case LibFunc_ZnwmSt11align_val_tRKSt9nothrow_t12__hot_cold_t:
- if (OptimizeExistingHotColdNew)
+ if (ShouldOptimizeExisting)
NewCall = emitHotColdNewAlignedNoThrow(
CI->getArgOperand(0), CI->getArgOperand(1), CI->getArgOperand(2), B,
TLI, LibFunc_ZnwmSt11align_val_tRKSt9nothrow_t12__hot_cold_t,
- HotCold);
+ getHotColdHintForExisting(HotCold));
break;
case LibFunc_ZnwmSt11align_val_tRKSt9nothrow_t:
NewCall = emitHotColdNewAlignedNoThrow(
CI->getArgOperand(0), CI->getArgOperand(1), CI->getArgOperand(2), B,
- TLI, LibFunc_ZnwmSt11align_val_tRKSt9nothrow_t12__hot_cold_t, HotCold);
+ TLI, LibFunc_ZnwmSt11align_val_tRKSt9nothrow_t12__hot_cold_t,
+ HotColdVal);
break;
case LibFunc_ZnamSt11align_val_tRKSt9nothrow_t12__hot_cold_t:
- if (OptimizeExistingHotColdNew)
+ if (ShouldOptimizeExisting)
NewCall = emitHotColdNewAlignedNoThrow(
CI->getArgOperand(0), CI->getArgOperand(1), CI->getArgOperand(2), B,
TLI, LibFunc_ZnamSt11align_val_tRKSt9nothrow_t12__hot_cold_t,
- HotCold);
+ getHotColdHintForExisting(HotCold));
break;
case LibFunc_ZnamSt11align_val_tRKSt9nothrow_t:
NewCall = emitHotColdNewAlignedNoThrow(
CI->getArgOperand(0), CI->getArgOperand(1), CI->getArgOperand(2), B,
- TLI, LibFunc_ZnamSt11align_val_tRKSt9nothrow_t12__hot_cold_t, HotCold);
+ TLI, LibFunc_ZnamSt11align_val_tRKSt9nothrow_t12__hot_cold_t,
+ HotColdVal);
break;
case LibFunc_size_returning_new:
NewCall = emitHotColdSizeReturningNew(CI->getArgOperand(0), B, TLI,
LibFunc_size_returning_new_hot_cold,
- HotCold);
+ HotColdVal);
break;
case LibFunc_size_returning_new_hot_cold:
- if (OptimizeExistingHotColdNew)
+ if (ShouldOptimizeExisting)
NewCall = emitHotColdSizeReturningNew(CI->getArgOperand(0), B, TLI,
LibFunc_size_returning_new_hot_cold,
- HotCold);
+ getHotColdHintForExisting(HotCold));
break;
case LibFunc_size_returning_new_aligned:
NewCall = emitHotColdSizeReturningNewAligned(
CI->getArgOperand(0), CI->getArgOperand(1), B, TLI,
- LibFunc_size_returning_new_aligned_hot_cold, HotCold);
+ LibFunc_size_returning_new_aligned_hot_cold, HotColdVal);
break;
case LibFunc_size_returning_new_aligned_hot_cold:
- if (OptimizeExistingHotColdNew)
+ if (ShouldOptimizeExisting)
NewCall = emitHotColdSizeReturningNewAligned(
CI->getArgOperand(0), CI->getArgOperand(1), B, TLI,
- LibFunc_size_returning_new_aligned_hot_cold, HotCold);
+ LibFunc_size_returning_new_aligned_hot_cold,
+ getHotColdHintForExisting(HotCold));
break;
default:
return nullptr;
diff --git a/llvm/test/Transforms/InstCombine/simplify-libcalls-new.ll b/llvm/test/Transforms/InstCombine/simplify-libcalls-new.ll
index 2765c75f4d5e0..545df33a389ff 100644
--- a/llvm/test/Transforms/InstCombine/simplify-libcalls-new.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-libcalls-new.ll
@@ -6,18 +6,24 @@
; OFF-LABEL: @new_hot_cold()
;; First check with the default hint values (254 = -2, 128 = -128, 222 = -34).
-; RUN: opt < %s -passes=instcombine -optimize-hot-cold-new -S | FileCheck %s --check-prefixes=HOTCOLD,NOBUILTIN-OFF -DCOLD=1 -DHOT=-2 -DNOTCOLD=-128 -DAMBIG=-34 -DPREVHINTCOLD=7 -DPREVHINTNOTCOLD=7 -DPREVHINTHOT=7 -DPREVHINTAMBIG=7
+; RUN: opt < %s -passes=instcombine -optimize-hot-cold-new -S | FileCheck %s --check-prefixes=HOTCOLD,NOBUILTIN-OFF,MIN-OFF -DCOLD=1 -DHOT=-2 -DNOTCOLD=-128 -DAMBIG=-34 -DPREVHINTCOLD=7 -DPREVHINTNOTCOLD=7 -DPREVHINTHOT=7 -DPREVHINTAMBIG=7 -DMINCOLD1=2 -DMINCOLD2=7 -DMINHOT1=-6 -DMINHOT2=50
;; Next check with the non-default cold and hot hint values (200 =-56).
-; RUN: opt < %s -passes=instcombine -optimize-hot-cold-new -cold-new-hint-value=5 -hot-new-hint-value=200 -notcold-new-hint-value=99 -ambiguous-new-hint-value=44 -S | FileCheck %s --check-prefixes=HOTCOLD,NOBUILTIN-OFF -DCOLD=5 -DHOT=-56 -DAMBIG=44 -DNOTCOLD=99 -DPREVHINTCOLD=7 -DPREVHINTNOTCOLD=7 -DPREVHINTHOT=7 -DPREVHINTAMBIG=7
+; RUN: opt < %s -passes=instcombine -optimize-hot-cold-new -cold-new-hint-value=5 -hot-new-hint-value=200 -notcold-new-hint-value=99 -ambiguous-new-hint-value=44 -S | FileCheck %s --check-prefixes=HOTCOLD,NOBUILTIN-OFF,MIN-OFF -DCOLD=5 -DHOT=-56 -DAMBIG=44 -DNOTCOLD=99 -DPREVHINTCOLD=7 -DPREVHINTNOTCOLD=7 -DPREVHINTHOT=7 -DPREVHINTAMBIG=7 -DMINCOLD1=2 -DMINCOLD2=7 -DMINHOT1=-6 -DMINHOT2=50
;; Next check with the same non-default cold and hot hint values (200 =-56),
;; but with transformation of nobuiltin calls enabled.
-; RUN: opt < %s -passes=instcombine -optimize-hot-cold-new -optimize-nobuiltin-hot-cold-new-new -cold-new-hint-value=5 -hot-new-hint-value=200 -notcold-new-hint-value=99 -ambiguous-new-hint-value=44 -S | FileCheck %s --check-prefixes=HOTCOLD,NOBUILTIN-ON -DCOLD=5 -DHOT=-56 -DAMBIG=44 -DNOTCOLD=99 -DPREVHINTCOLD=7 -DPREVHINTNOTCOLD=7 -DPREVHINTHOT=7 -DPREVHINTAMBIG=7
+; RUN: opt < %s -passes=instcombine -optimize-hot-cold-new -optimize-nobuiltin-hot-cold-new-new -cold-new-hint-value=5 -hot-new-hint-value=200 -notcold-new-hint-value=99 -ambiguous-new-hint-value=44 -S | FileCheck %s --check-prefixes=HOTCOLD,NOBUILTIN-ON,MIN-OFF -DCOLD=5 -DHOT=-56 -DAMBIG=44 -DNOTCOLD=99 -DPREVHINTCOLD=7 -DPREVHINTNOTCOLD=7 -DPREVHINTHOT=7 -DPREVHINTAMBIG=7 -DMINCOLD1=2 -DMINCOLD2=7 -DMINHOT1=-6 -DMINHOT2=50
;; Try again with the non-default cold and hot hint values (200 =-56), and this
-;; time specify that existing hints should be updated.
-; RUN: opt < %s -passes=instcombine -optimize-hot-cold-new -cold-new-hint-value=5 -notcold-new-hint-value=100 -hot-new-hint-value=200 -ambiguous-new-hint-value=44 -optimize-existing-hot-cold-new -S | FileCheck %s --check-prefixes=HOTCOLD,NOBUILTIN-OFF -DCOLD=5 -DHOT=-56 -DNOTCOLD=100 -DAMBIG=44 -DPREVHINTCOLD=5 -DPREVHINTNOTCOLD=100 -DPREVHINTHOT=-56 -DPREVHINTAMBIG=44
+;; time specify that existing hints should be updated (using default/implicit value,
+;; explicit always, cold, none, and with min hint enabled).
+; RUN: opt < %s -passes=instcombine -optimize-hot-cold-new -cold-new-hint-value=5 -notcold-new-hint-value=100 -hot-new-hint-value=200 -ambiguous-new-hint-value=44 -optimize-existing-hot-cold-new -S | FileCheck %s --check-prefixes=HOTCOLD,NOBUILTIN-OFF,NO-MIN-ALWAYS -DCOLD=5 -DHOT=-56 -DNOTCOLD=100 -DAMBIG=44 -DPREVHINTCOLD=5 -DPREVHINTNOTCOLD=100 -DPREVHINTHOT=-56 -DPREVHINTAMBIG=44 -DMINCOLD1=5 -DMINCOLD2=5 -DMINHOT1=-56 -DMINHOT2=-56
+; RUN: opt < %s -passes=instcombine -optimize-hot-cold-new -cold-new-hint-value=5 -notcold-new-hint-value=100 -hot-new-hint-value=200 -ambiguous-new-hint-value=44 -optimize-existing-hot-cold-new=always -S | FileCheck %s --check-prefixes=HOTCOLD,NOBUILTIN-OFF,NO-MIN-ALWAYS -DCOLD=5 -DHOT=-56 -DNOTCOLD=100 -DAMBIG=44 -DPREVHINTCOLD=5 -DPREVHINTNOTCOLD=100 -DPREVHINTHOT=-56 -DPREVHINTAMBIG=44 -DMINCOLD1=5 -DMINCOLD2=5 -DMINHOT1=-56 -DMINHOT2=-56
+; RUN: opt < %s -passes=instcombine -optimize-hot-cold-new -cold-new-hint-value=5 -notcold-new-hint-value=100 -hot-new-hint-value=200 -ambiguous-new-hint-value=44 -optimize-existing-hot-cold-new=cold -S | FileCheck %s --check-prefixes=HOTCOLD,NOBUILTIN-OFF,NO-MIN-COLD -DCOLD=5 -DHOT=-56 -DNOTCOLD=100 -DAMBIG=44 -DPREVHINTCOLD=5 -DPREVHINTNOTCOLD=7 -DPREVHINTHOT=7 -DPREVHINTAMBIG=7 -DMINCOLD1=5 -DMINCOLD2=5 -DMINHOT1=-6 -DMINHOT2=50
+; RUN: opt < %s -passes=instcombine -optimize-hot-cold-new -cold-new-hint-value=5 -notcold-new-hint-value=100 -hot-new-hint-value=200 -ambiguous-new-hint-value=44 -optimize-existing-hot-cold-new=none -S | FileCheck %s --check-prefixes=HOTCOLD,NOBUILTIN-OFF,MIN-OFF -DCOLD=5 -DHOT=-56 -DNOTCOLD=100 -DAMBIG=44 -DPREVHINTCOLD=7 -DPREVHINTNOTCOLD=7 -DPREVHINTHOT=7 -DPREVHINTAMBIG=7 -DMINCOLD1=2 -DMINCOLD2=7 -DMINHOT1=-6 -DMINHOT2=50
+; RUN: opt < %s -passes=instcombine -optimize-hot-cold-new -cold-new-hint-value=5 -notcold-new-hint-value=100 -hot-new-hint-value=200 -ambiguous-new-hint-value=44 -optimize-existing-hot-cold-new -min-existing-hot-cold-new-hint -S | FileCheck %s --check-prefixes=HOTCOLD,NOBUILTIN-OFF,MIN-ALWAYS -DCOLD=5 -DHOT=-56 -DNOTCOLD=100 -DAMBIG=44 -DPREVHINTCOLD=5 -DPREVHINTNOTCOLD=7 -DPREVHINTHOT=7 -DPREVHINTAMBIG=7 -DMINCOLD1=2 -DMINCOLD2=5 -DMINHOT1=-56 -DMINHOT2=50
+; RUN: opt < %s -passes=instcombine -optimize-hot-cold-new -cold-new-hint-value=5 -notcold-new-hint-value=100 -hot-new-hint-value=200 -ambiguous-new-hint-value=44 -optimize-existing-hot-cold-new=cold -min-existing-hot-cold-new-hint -S | FileCheck %s --check-prefixes=HOTCOLD,NOBUILTIN-OFF,MIN-COLD -DCOLD=5 -DHOT=-56 -DNOTCOLD=100 -DAMBIG=44 -DPREVHINTCOLD=5 -DPREVHINTNOTCOLD=7 -DPREVHINTHOT=7 -DPREVHINTAMBIG=7 -DMINCOLD1=2 -DMINCOLD2=5 -DMINHOT1=-6 -DMINHOT2=50
;; Make sure that values not in 0..255 are flagged with an error
; RUN: not opt < %s -passes=instcombine -optimize-hot-cold-new -cold-new-hint-value=256 -S 2>&1 | FileCheck %s --check-prefix=ERROR
@@ -620,6 +626,54 @@ define void @new_alloc_token() {
ret void
}
+;; Check that -min-existing-hot-cold-new-hint takes the min of existing and compiler hints.
+; HOTCOLD-LABEL: @new_hot_cold_min()
+define void @new_hot_cold_min() {
+ ;; Existing hint 2 < cold hint 5 -> min is 2
+ ; HOTCOLD: @_Znwm12__hot_cold_t(i64 10, i8 [[MINCOLD1]])
+ %call = call ptr @_Znwm12__hot_cold_t(i64 10, i8 2) #0
+ call void @dummy(ptr %call)
+ ;; Existing hint 7 > cold hint 5 -> min is 5
+ ; HOTCOLD: @_Znwm12__hot_cold_t(i64 10, i8 [[MINCOLD2]])
+ %call1 = call ptr @_Znwm12__hot_cold_t(i64 10, i8 7) #0
+ call void @dummy(ptr %call1)
+ ;; Existing hint 250 > hot hint 200 (-56) -> min is 200 (-56)
+ ; HOTCOLD: @_Znwm12__hot_cold_t(i64 10, i8 [[MINHOT1]])
+ %call2 = call ptr @_Znwm12__hot_cold_t(i64 10, i8 -6) #2
+ call void @dummy(ptr %call2)
+ ;; Existing hint 50 < hot hint 200 (-56) -> min is 50
+ ; HOTCOLD: @_Znwm12__hot_cold_t(i64 10, i8 [[MINHOT2]])
+ %call3 = call ptr @_Znwm12__hot_cold_t(i64 10, i8 50) #2
+ call void @dummy(ptr %call3)
+ ret void
+}
+
+;; Check that -min-existing-hot-cold-new-hint handles non-constant dynamic hints with umin intrinsic.
+; HOTCOLD-LABEL: @new_hot_cold_dynamic(
+; HOTCOLD-SAME: i8 [[HINT:%[a-zA-Z0-9_]+]])
+define void @new_hot_cold_dynamic(i8 %hint) {
+ ;; Cold allocation
+ ; MIN-OFF: @_Znwm12__hot_cold_t(i64 10, i8 [[HINT]])
+ ; NO-MIN-ALWAYS: @_Znwm12__hot_cold_t(i64 10, i8 [[COLD]])
+ ; NO-MIN-COLD: @_Znwm12__hot_cold_t(i64 10, i8 [[COLD]])
+ ; MIN-ALWAYS: [[UMIN:%[a-zA-Z0-9_]+]] = call i8 @llvm.umin.i8(i8 [[HINT]], i8 [[COLD]])
+ ; MIN-ALWAYS: @_Znwm12__hot_cold_t(i64 10, i8 [[UMIN]])
+ ; MIN-COLD: [[UMIN:%[a-zA-Z0-9_]+]] = call i8 @llvm.umin.i8(i8 [[HINT]], i8 [[COLD]])
+ ; MIN-COLD: @_Znwm12__hot_cold_t(i64 10, i8 [[UMIN]])
+ %call = call ptr @_Znwm12__hot_cold_t(i64 10, i8 %hint) #0
+ call void @dummy(ptr %call)
+ ;; Hot allocation
+ ; MIN-OFF: @_Znwm12__hot_cold_t(i64 10, i8 [[HINT]])
+ ; NO-MIN-ALWAYS: @_Znwm12__hot_cold_t(i64 10, i8 [[HOT]])
+ ; NO-MIN-COLD: @_Znwm12__hot_cold_t(i64 10, i8 [[HINT]])
+ ; MIN-ALWAYS: [[UMIN2:%[a-zA-Z0-9_]+]] = call i8 @llvm.umin.i8(i8 [[HINT]], i8 [[HOT]])
+ ; MIN-ALWAYS: @_Znwm12__hot_cold_t(i64 10, i8 [[UMIN2]])
+ ; MIN-COLD: @_Znwm12__hot_cold_t(i64 10, i8 [[HINT]])
+ %call1 = call ptr @_Znwm12__hot_cold_t(i64 10, i8 %hint) #2
+ call void @dummy(ptr %call1)
+ ret void
+}
+
;; So that instcombine doesn't optimize out the call.
declare void @dummy(ptr)
>From 59bc4913ef55ecfefafef3dd51ddb103d99d0bda Mon Sep 17 00:00:00 2001
From: Teresa Johnson <tejohnson at google.com>
Date: Tue, 25 Aug 2026 17:09:38 -0700
Subject: [PATCH 2/3] Address comments
---
.../lib/Transforms/Utils/SimplifyLibCalls.cpp | 27 +++++++++++--------
1 file changed, 16 insertions(+), 11 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index 0275d01fed134..41c7da8592ec7 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -1834,19 +1834,24 @@ Value *LibCallSimplifier::optimizeNew(CallInst *CI, IRBuilderBase &B,
else
return nullptr;
- bool ShouldOptimizeExisting =
+ bool ShouldOptimizeExistingHotColdNew =
OptimizeExistingHotColdNew == OptimizeExistingHotColdNewKind::Always ||
(OptimizeExistingHotColdNew == OptimizeExistingHotColdNewKind::Cold &&
IsCold);
Value *HotColdVal = B.getInt8(HotCold);
auto getHotColdHintForExisting = [&](uint8_t HotCold) -> Value * {
+ // If not taking the minimum, simply use the compiler hint value.
if (!MinExistingHotColdNewHint)
return HotColdVal;
Value *ExistingHint = CI->getArgOperand(CI->arg_size() - 1);
+ // If the existing hint is a compile-time constant, evaluate the minimum
+ // at compile time.
if (auto *CIHint = dyn_cast<ConstantInt>(ExistingHint))
return B.getInt8(
std::min(HotCold, static_cast<uint8_t>(CIHint->getZExtValue())));
+ // If the existing hint is a dynamic/runtime value, emit a umin intrinsic
+ // to compute the minimum at runtime.
if (ExistingHint->getType() != B.getInt8Ty())
ExistingHint = B.CreateTruncOrBitCast(ExistingHint, B.getInt8Ty());
return B.CreateBinaryIntrinsic(Intrinsic::umin, ExistingHint, HotColdVal);
@@ -1862,7 +1867,7 @@ Value *LibCallSimplifier::optimizeNew(CallInst *CI, IRBuilderBase &B,
Value *NewCall = nullptr;
switch (Func) {
case LibFunc_Znwm12__hot_cold_t:
- if (ShouldOptimizeExisting)
+ if (ShouldOptimizeExistingHotColdNew)
NewCall = emitHotColdNew(CI->getArgOperand(0), B, TLI,
LibFunc_Znwm12__hot_cold_t,
getHotColdHintForExisting(HotCold));
@@ -1872,7 +1877,7 @@ Value *LibCallSimplifier::optimizeNew(CallInst *CI, IRBuilderBase &B,
LibFunc_Znwm12__hot_cold_t, HotColdVal);
break;
case LibFunc_Znam12__hot_cold_t:
- if (ShouldOptimizeExisting)
+ if (ShouldOptimizeExistingHotColdNew)
NewCall = emitHotColdNew(CI->getArgOperand(0), B, TLI,
LibFunc_Znam12__hot_cold_t,
getHotColdHintForExisting(HotCold));
@@ -1882,7 +1887,7 @@ Value *LibCallSimplifier::optimizeNew(CallInst *CI, IRBuilderBase &B,
LibFunc_Znam12__hot_cold_t, HotColdVal);
break;
case LibFunc_ZnwmRKSt9nothrow_t12__hot_cold_t:
- if (ShouldOptimizeExisting)
+ if (ShouldOptimizeExistingHotColdNew)
NewCall =
emitHotColdNewNoThrow(CI->getArgOperand(0), CI->getArgOperand(1), B,
TLI, LibFunc_ZnwmRKSt9nothrow_t12__hot_cold_t,
@@ -1894,7 +1899,7 @@ Value *LibCallSimplifier::optimizeNew(CallInst *CI, IRBuilderBase &B,
LibFunc_ZnwmRKSt9nothrow_t12__hot_cold_t, HotColdVal);
break;
case LibFunc_ZnamRKSt9nothrow_t12__hot_cold_t:
- if (ShouldOptimizeExisting)
+ if (ShouldOptimizeExistingHotColdNew)
NewCall =
emitHotColdNewNoThrow(CI->getArgOperand(0), CI->getArgOperand(1), B,
TLI, LibFunc_ZnamRKSt9nothrow_t12__hot_cold_t,
@@ -1906,7 +1911,7 @@ Value *LibCallSimplifier::optimizeNew(CallInst *CI, IRBuilderBase &B,
LibFunc_ZnamRKSt9nothrow_t12__hot_cold_t, HotColdVal);
break;
case LibFunc_ZnwmSt11align_val_t12__hot_cold_t:
- if (ShouldOptimizeExisting)
+ if (ShouldOptimizeExistingHotColdNew)
NewCall =
emitHotColdNewAligned(CI->getArgOperand(0), CI->getArgOperand(1), B,
TLI, LibFunc_ZnwmSt11align_val_t12__hot_cold_t,
@@ -1918,7 +1923,7 @@ Value *LibCallSimplifier::optimizeNew(CallInst *CI, IRBuilderBase &B,
LibFunc_ZnwmSt11align_val_t12__hot_cold_t, HotColdVal);
break;
case LibFunc_ZnamSt11align_val_t12__hot_cold_t:
- if (ShouldOptimizeExisting)
+ if (ShouldOptimizeExistingHotColdNew)
NewCall =
emitHotColdNewAligned(CI->getArgOperand(0), CI->getArgOperand(1), B,
TLI, LibFunc_ZnamSt11align_val_t12__hot_cold_t,
@@ -1930,7 +1935,7 @@ Value *LibCallSimplifier::optimizeNew(CallInst *CI, IRBuilderBase &B,
LibFunc_ZnamSt11align_val_t12__hot_cold_t, HotColdVal);
break;
case LibFunc_ZnwmSt11align_val_tRKSt9nothrow_t12__hot_cold_t:
- if (ShouldOptimizeExisting)
+ if (ShouldOptimizeExistingHotColdNew)
NewCall = emitHotColdNewAlignedNoThrow(
CI->getArgOperand(0), CI->getArgOperand(1), CI->getArgOperand(2), B,
TLI, LibFunc_ZnwmSt11align_val_tRKSt9nothrow_t12__hot_cold_t,
@@ -1943,7 +1948,7 @@ Value *LibCallSimplifier::optimizeNew(CallInst *CI, IRBuilderBase &B,
HotColdVal);
break;
case LibFunc_ZnamSt11align_val_tRKSt9nothrow_t12__hot_cold_t:
- if (ShouldOptimizeExisting)
+ if (ShouldOptimizeExistingHotColdNew)
NewCall = emitHotColdNewAlignedNoThrow(
CI->getArgOperand(0), CI->getArgOperand(1), CI->getArgOperand(2), B,
TLI, LibFunc_ZnamSt11align_val_tRKSt9nothrow_t12__hot_cold_t,
@@ -1961,7 +1966,7 @@ Value *LibCallSimplifier::optimizeNew(CallInst *CI, IRBuilderBase &B,
HotColdVal);
break;
case LibFunc_size_returning_new_hot_cold:
- if (ShouldOptimizeExisting)
+ if (ShouldOptimizeExistingHotColdNew)
NewCall = emitHotColdSizeReturningNew(CI->getArgOperand(0), B, TLI,
LibFunc_size_returning_new_hot_cold,
getHotColdHintForExisting(HotCold));
@@ -1972,7 +1977,7 @@ Value *LibCallSimplifier::optimizeNew(CallInst *CI, IRBuilderBase &B,
LibFunc_size_returning_new_aligned_hot_cold, HotColdVal);
break;
case LibFunc_size_returning_new_aligned_hot_cold:
- if (ShouldOptimizeExisting)
+ if (ShouldOptimizeExistingHotColdNew)
NewCall = emitHotColdSizeReturningNewAligned(
CI->getArgOperand(0), CI->getArgOperand(1), B, TLI,
LibFunc_size_returning_new_aligned_hot_cold,
>From 0544e48c8c4476952cf9e4b8806a3c7a60914775 Mon Sep 17 00:00:00 2001
From: Teresa Johnson <tejohnson at google.com>
Date: Tue, 25 Aug 2026 19:52:03 -0700
Subject: [PATCH 3/3] Address comments
---
llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index 41c7da8592ec7..1c92409b3379b 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -1845,15 +1845,11 @@ Value *LibCallSimplifier::optimizeNew(CallInst *CI, IRBuilderBase &B,
if (!MinExistingHotColdNewHint)
return HotColdVal;
Value *ExistingHint = CI->getArgOperand(CI->arg_size() - 1);
- // If the existing hint is a compile-time constant, evaluate the minimum
- // at compile time.
- if (auto *CIHint = dyn_cast<ConstantInt>(ExistingHint))
- return B.getInt8(
- std::min(HotCold, static_cast<uint8_t>(CIHint->getZExtValue())));
- // If the existing hint is a dynamic/runtime value, emit a umin intrinsic
- // to compute the minimum at runtime.
if (ExistingHint->getType() != B.getInt8Ty())
ExistingHint = B.CreateTruncOrBitCast(ExistingHint, B.getInt8Ty());
+ // Emit a umin intrinsic to take the minimum of the existing hint and the
+ // compiler hint. When the existing hint is a compile-time constant, the
+ // IRBuilder folder will automatically constant-fold this into a constant.
return B.CreateBinaryIntrinsic(Intrinsic::umin, ExistingHint, HotColdVal);
};
More information about the llvm-commits
mailing list