[llvm] [ConstantFolding] Implement canConstantFoldCallTo() using TLI (PR #221903)

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 8 01:00:37 PDT 2026


https://github.com/nikic updated https://github.com/llvm/llvm-project/pull/221903

>From cfce403e5c8b25fbcb9578161dd5ae461ff274e3 Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Mon, 7 Sep 2026 17:10:14 +0200
Subject: [PATCH 1/2] [ConstantFolding] Implement canConstantFoldCallTo() using
 TLI

This did some odd matching on string names. Use TLI instead,
matching the actual constant folding logic.

I've adjusted callers to pass TLI to canConstantFoldCallTo() if
they also pass TLI to the later constant folding call.
---
 llvm/include/llvm/Analysis/ConstantFolding.h |   3 +-
 llvm/lib/Analysis/ConstantFolding.cpp        | 180 ++++++++++---------
 llvm/lib/Analysis/InstructionSimplify.cpp    |   2 +-
 llvm/lib/Analysis/ScalarEvolution.cpp        |  32 ++--
 llvm/lib/Transforms/Utils/SCCPSolver.cpp     |   8 +-
 5 files changed, 127 insertions(+), 98 deletions(-)

diff --git a/llvm/include/llvm/Analysis/ConstantFolding.h b/llvm/include/llvm/Analysis/ConstantFolding.h
index 87867d6f5303e..ddbeecf0b43f0 100644
--- a/llvm/include/llvm/Analysis/ConstantFolding.h
+++ b/llvm/include/llvm/Analysis/ConstantFolding.h
@@ -160,7 +160,8 @@ LLVM_ABI Constant *ConstantFoldLoadFromUniformValue(Constant *C, Type *Ty,
 
 /// canConstantFoldCallTo - Return true if its even possible to fold a call to
 /// the specified function.
-LLVM_ABI bool canConstantFoldCallTo(const CallBase *Call, const Function *F);
+LLVM_ABI bool canConstantFoldCallTo(const CallBase *Call, const Function *F,
+                                    const TargetLibraryInfo *TLI = nullptr);
 
 /// ConstantFoldCall - Attempt to constant fold a call to the specified function
 /// with the specified arguments, returning null if unsuccessful.
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index c819e188cac95..33be1d5bc9e39 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -1188,7 +1188,7 @@ Constant *ConstantFoldInstOperandsImpl(const Value *InstOrCE, unsigned Opcode,
   case Instruction::Call:
     if (auto *F = dyn_cast<Function>(Ops.back())) {
       const auto *Call = cast<CallBase>(InstOrCE);
-      if (canConstantFoldCallTo(Call, F))
+      if (canConstantFoldCallTo(Call, F, TLI))
         return ConstantFoldCall(Call, F, Ops.slice(0, Ops.size() - 1), TLI,
                                 AllowNonDeterministic);
     }
@@ -2100,7 +2100,8 @@ static bool anyTypeContainsFP(Type *RetTy, ArrayRef<Value *> Ops) {
          });
 }
 
-bool llvm::canConstantFoldCallTo(const CallBase *Call, const Function *F) {
+bool llvm::canConstantFoldCallTo(const CallBase *Call, const Function *F,
+                                 const TargetLibraryInfo *TLI) {
   if (Call->isNoBuiltin())
     return false;
   if (Call->getFunctionType() != F->getFunctionType())
@@ -2119,89 +2120,106 @@ bool llvm::canConstantFoldCallTo(const CallBase *Call, const Function *F) {
   if (F->getIntrinsicID() != Intrinsic::not_intrinsic)
     return canConstantFoldIntrinsic(F->getIntrinsicID(), Call->isStrictFP());
 
-  if (!F->hasName() || Call->isStrictFP())
+  if (!TLI || !F->hasName() || Call->isStrictFP())
     return false;
 
-  // In these cases, the check of the length is required.  We don't want to
-  // return true for a name like "cos\0blah" which strcmp would return equal to
-  // "cos", but has length 8.
-  StringRef Name = F->getName();
-  switch (Name[0]) {
+  LibFunc Func = TLI->getLibFunc(*F);
+  if (Func == NotLibFunc)
+    return false;
+
+  switch (Func) {
+  case LibFunc_acos:
+  case LibFunc_acosf:
+  case LibFunc_acos_finite:
+  case LibFunc_acosf_finite:
+  case LibFunc_asin:
+  case LibFunc_asinf:
+  case LibFunc_asin_finite:
+  case LibFunc_asinf_finite:
+  case LibFunc_atan:
+  case LibFunc_atanf:
+  case LibFunc_atan2:
+  case LibFunc_atan2f:
+  case LibFunc_atan2_finite:
+  case LibFunc_atan2f_finite:
+  case LibFunc_ceil:
+  case LibFunc_ceilf:
+  case LibFunc_cosh:
+  case LibFunc_coshf:
+  case LibFunc_cosh_finite:
+  case LibFunc_coshf_finite:
+  case LibFunc_cos:
+  case LibFunc_cosf:
+  case LibFunc_erf:
+  case LibFunc_erff:
+  case LibFunc_exp:
+  case LibFunc_expf:
+  case LibFunc_exp_finite:
+  case LibFunc_expf_finite:
+  case LibFunc_exp2:
+  case LibFunc_exp2f:
+  case LibFunc_exp2_finite:
+  case LibFunc_exp2f_finite:
+  case LibFunc_fabs:
+  case LibFunc_fabsf:
+  case LibFunc_floor:
+  case LibFunc_floorf:
+  case LibFunc_fmod:
+  case LibFunc_fmodf:
+  case LibFunc_ilogb:
+  case LibFunc_ilogbf:
+  case LibFunc_log:
+  case LibFunc_logf:
+  case LibFunc_log_finite:
+  case LibFunc_logf_finite:
+  case LibFunc_logb:
+  case LibFunc_logbf:
+  case LibFunc_logl:
+  case LibFunc_log2:
+  case LibFunc_log2f:
+  case LibFunc_log2_finite:
+  case LibFunc_log2f_finite:
+  case LibFunc_log10:
+  case LibFunc_log10f:
+  case LibFunc_log10_finite:
+  case LibFunc_log10f_finite:
+  case LibFunc_log1p:
+  case LibFunc_log1pf:
+  case LibFunc_nearbyint:
+  case LibFunc_nearbyintf:
+  case LibFunc_nextafter:
+  case LibFunc_nextafterf:
+  case LibFunc_nexttoward:
+  case LibFunc_nexttowardf:
+  case LibFunc_pow:
+  case LibFunc_powf:
+  case LibFunc_pow_finite:
+  case LibFunc_powf_finite:
+  case LibFunc_remainder:
+  case LibFunc_remainderf:
+  case LibFunc_rint:
+  case LibFunc_rintf:
+  case LibFunc_round:
+  case LibFunc_roundf:
+  case LibFunc_roundeven:
+  case LibFunc_roundevenf:
+  case LibFunc_sin:
+  case LibFunc_sinf:
+  case LibFunc_sinh:
+  case LibFunc_sinhf:
+  case LibFunc_sinh_finite:
+  case LibFunc_sinhf_finite:
+  case LibFunc_sqrt:
+  case LibFunc_sqrtf:
+  case LibFunc_tan:
+  case LibFunc_tanf:
+  case LibFunc_tanh:
+  case LibFunc_tanhf:
+  case LibFunc_trunc:
+  case LibFunc_truncf:
+    return true;
   default:
     return false;
-    // clang-format off
-  case 'a':
-    return Name == "acos" || Name == "acosf" ||
-           Name == "asin" || Name == "asinf" ||
-           Name == "atan" || Name == "atanf" ||
-           Name == "atan2" || Name == "atan2f";
-  case 'c':
-    return Name == "ceil" || Name == "ceilf" ||
-           Name == "cos" || Name == "cosf" ||
-           Name == "cosh" || Name == "coshf";
-  case 'e':
-    return Name == "exp" || Name == "expf" || Name == "exp2" ||
-           Name == "exp2f" || Name == "erf" || Name == "erff";
-  case 'f':
-    return Name == "fabs" || Name == "fabsf" ||
-           Name == "floor" || Name == "floorf" ||
-           Name == "fmod" || Name == "fmodf";
-  case 'i':
-    return Name == "ilogb" || Name == "ilogbf";
-  case 'l':
-    return Name == "log" || Name == "logf" || Name == "logl" ||
-           Name == "log2" || Name == "log2f" || Name == "log10" ||
-           Name == "log10f" || Name == "logb" || Name == "logbf" ||
-           Name == "log1p" || Name == "log1pf";
-  case 'n':
-    return Name == "nearbyint" || Name == "nearbyintf" || Name == "nextafter" ||
-           Name == "nextafterf" || Name == "nexttoward" ||
-           Name == "nexttowardf";
-  case 'p':
-    return Name == "pow" || Name == "powf";
-  case 'r':
-    return Name == "remainder" || Name == "remainderf" ||
-           Name == "rint" || Name == "rintf" ||
-           Name == "round" || Name == "roundf" ||
-           Name == "roundeven" || Name == "roundevenf";
-  case 's':
-    return Name == "sin" || Name == "sinf" ||
-           Name == "sinh" || Name == "sinhf" ||
-           Name == "sqrt" || Name == "sqrtf";
-  case 't':
-    return Name == "tan" || Name == "tanf" ||
-           Name == "tanh" || Name == "tanhf" ||
-           Name == "trunc" || Name == "truncf";
-  case '_':
-    // Check for various function names that get used for the math functions
-    // when the header files are preprocessed with the macro
-    // __FINITE_MATH_ONLY__ enabled.
-    // The '12' here is the length of the shortest name that can match.
-    // We need to check the size before looking at Name[1] and Name[2]
-    // so we may as well check a limit that will eliminate mismatches.
-    if (Name.size() < 12 || Name[1] != '_')
-      return false;
-    switch (Name[2]) {
-    default:
-      return false;
-    case 'a':
-      return Name == "__acos_finite" || Name == "__acosf_finite" ||
-             Name == "__asin_finite" || Name == "__asinf_finite" ||
-             Name == "__atan2_finite" || Name == "__atan2f_finite";
-    case 'c':
-      return Name == "__cosh_finite" || Name == "__coshf_finite";
-    case 'e':
-      return Name == "__exp_finite" || Name == "__expf_finite" ||
-             Name == "__exp2_finite" || Name == "__exp2f_finite";
-    case 'l':
-      return Name == "__log_finite" || Name == "__logf_finite" ||
-             Name == "__log10_finite" || Name == "__log10f_finite";
-    case 'p':
-      return Name == "__pow_finite" || Name == "__powf_finite";
-    case 's':
-      return Name == "__sinh_finite" || Name == "__sinhf_finite";
-    }
-    // clang-format on
   }
 }
 
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index d7b11f8cd2f7b..63695c3f3bb97 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -7617,7 +7617,7 @@ static Value *simplifyIntrinsic(CallBase *Call, ArrayRef<Value *> Args,
 static Value *tryConstantFoldCall(CallBase *Call, ArrayRef<Value *> Args,
                                   const SimplifyQuery &Q) {
   auto *F = Call->getCalledFunction();
-  if (!F || !canConstantFoldCallTo(Call, F))
+  if (!F || !canConstantFoldCallTo(Call, F, Q.TLI))
     return nullptr;
 
   SmallVector<Constant *, 4> ConstantArgs;
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 90dafda95ee30..9211b3d60b6ed 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -9734,7 +9734,8 @@ ScalarEvolution::ExitLimit ScalarEvolution::computeShiftCompareExitLimit(
 
 /// Return true if we can constant fold an instruction of the specified type,
 /// assuming that all operands were constants.
-static bool CanConstantFold(const Instruction *I) {
+static bool canConstantFold(const Instruction *I,
+                            const TargetLibraryInfo *TLI) {
   if (isa<BinaryOperator, UnaryOperator, GEPOperator, FreezeInst, CmpInst,
           SelectInst, CastInst, LoadInst, ExtractElementInst, InsertElementInst,
           ExtractValueInst, InsertValueInst>(I))
@@ -9742,13 +9743,14 @@ static bool CanConstantFold(const Instruction *I) {
 
   if (const CallInst *CI = dyn_cast<CallInst>(I))
     if (const Function *F = CI->getCalledFunction())
-      return canConstantFoldCallTo(CI, F);
+      return canConstantFoldCallTo(CI, F, TLI);
   return false;
 }
 
 /// Determine whether this instruction can constant evolve within this loop
 /// assuming its operands can all constant evolve.
-static bool canConstantEvolve(Instruction *I, const Loop *L) {
+static bool canConstantEvolve(Instruction *I, const Loop *L,
+                              const TargetLibraryInfo *TLI) {
   // An instruction outside of the loop can't be derived from a loop PHI.
   if (!L->contains(I)) return false;
 
@@ -9760,7 +9762,7 @@ static bool canConstantEvolve(Instruction *I, const Loop *L) {
 
   // If we won't be able to constant fold this expression even if the operands
   // are constants, bail early.
-  return CanConstantFold(I);
+  return canConstantFold(I, TLI);
 }
 
 /// getConstantEvolvingPHIOperands - Implement getConstantEvolvingPHI by
@@ -9768,7 +9770,7 @@ static bool canConstantEvolve(Instruction *I, const Loop *L) {
 static PHINode *
 getConstantEvolvingPHIOperands(Instruction *UseInst, const Loop *L,
                                DenseMap<Instruction *, PHINode *> &PHIMap,
-                               unsigned Depth) {
+                               const TargetLibraryInfo *TLI, unsigned Depth) {
   if (Depth > MaxConstantEvolvingDepth)
     return nullptr;
 
@@ -9779,7 +9781,8 @@ getConstantEvolvingPHIOperands(Instruction *UseInst, const Loop *L,
     if (isa<Constant>(Op)) continue;
 
     Instruction *OpInst = dyn_cast<Instruction>(Op);
-    if (!OpInst || !canConstantEvolve(OpInst, L)) return nullptr;
+    if (!OpInst || !canConstantEvolve(OpInst, L, TLI))
+      return nullptr;
 
     PHINode *P = dyn_cast<PHINode>(OpInst);
     if (!P)
@@ -9790,7 +9793,7 @@ getConstantEvolvingPHIOperands(Instruction *UseInst, const Loop *L,
     if (!P) {
       // Recurse and memoize the results, whether a phi is found or not.
       // This recursive call invalidates pointers into PHIMap.
-      P = getConstantEvolvingPHIOperands(OpInst, L, PHIMap, Depth + 1);
+      P = getConstantEvolvingPHIOperands(OpInst, L, PHIMap, TLI, Depth + 1);
       PHIMap[OpInst] = P;
     }
     if (!P)
@@ -9808,16 +9811,18 @@ getConstantEvolvingPHIOperands(Instruction *UseInst, const Loop *L,
 /// way, but the operands of an operation must either be constants or a value
 /// derived from a constant PHI.  If this expression does not fit with these
 /// constraints, return null.
-static PHINode *getConstantEvolvingPHI(Value *V, const Loop *L) {
+static PHINode *getConstantEvolvingPHI(Value *V, const Loop *L,
+                                       const TargetLibraryInfo *TLI) {
   Instruction *I = dyn_cast<Instruction>(V);
-  if (!I || !canConstantEvolve(I, L)) return nullptr;
+  if (!I || !canConstantEvolve(I, L, TLI))
+    return nullptr;
 
   if (PHINode *PN = dyn_cast<PHINode>(I))
     return PN;
 
   // Record non-constant instructions contained by the loop.
   DenseMap<Instruction *, PHINode *> PHIMap;
-  return getConstantEvolvingPHIOperands(I, L, PHIMap, 0);
+  return getConstantEvolvingPHIOperands(I, L, PHIMap, TLI, 0);
 }
 
 /// EvaluateExpression - Given an expression that passes the
@@ -9837,7 +9842,8 @@ static Constant *EvaluateExpression(Value *V, const Loop *L,
 
   // An instruction inside the loop depends on a value outside the loop that we
   // weren't given a mapping for, or a value such as a call inside the loop.
-  if (!canConstantEvolve(I, L)) return nullptr;
+  if (!canConstantEvolve(I, L, TLI))
+    return nullptr;
 
   // An unmapped PHI can be due to a branch or another loop inside this loop,
   // or due to this not being the initial iteration through a loop where we
@@ -9977,7 +9983,7 @@ ScalarEvolution::getConstantEvolutionLoopExitValue(PHINode *PN,
 const SCEV *ScalarEvolution::computeExitCountExhaustively(const Loop *L,
                                                           Value *Cond,
                                                           bool ExitWhen) {
-  PHINode *PN = getConstantEvolvingPHI(Cond, L);
+  PHINode *PN = getConstantEvolvingPHI(Cond, L, &TLI);
   if (!PN) return getCouldNotCompute();
 
   // If the loop is canonicalized, the PHI will have exactly two entries.
@@ -10312,7 +10318,7 @@ SCEVUse ScalarEvolution::computeSCEVAtScope(const SCEV *V, const Loop *L) {
     // into a SCEV.  Check to see if it's possible to symbolically evaluate
     // the arguments into constants, and if so, try to constant propagate the
     // result.  This is particularly useful for computing loop exit values.
-    if (!CanConstantFold(I))
+    if (!canConstantFold(I, &TLI))
       return V; // This is some other type of SCEVUnknown, just return it.
 
     SmallVector<Constant *, 4> Operands;
diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
index 15f96c7407f1d..c24e59aac14a5 100644
--- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp
+++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
@@ -2010,9 +2010,13 @@ void SCCPInstVisitor::handleCallOverdefined(CallBase &CB) {
   if (CB.getType()->isStructTy())
     return (void)markOverdefined(&CB);
 
+  if (!F || !F->isDeclaration())
+    return (void)mergeInValue(ValueState[&CB], &CB, getValueFromMetadata(&CB));
+
   // Otherwise, if we have a single return value case, and if the function is
   // a declaration, maybe we can constant fold it.
-  if (F && F->isDeclaration() && canConstantFoldCallTo(&CB, F)) {
+  const TargetLibraryInfo *TLI = &GetTLI(*F);
+  if (canConstantFoldCallTo(&CB, F, TLI)) {
     SmallVector<Constant *, 8> Operands;
     for (const Use &A : CB.args()) {
       if (A.get()->getType()->isStructTy())
@@ -2034,7 +2038,7 @@ void SCCPInstVisitor::handleCallOverdefined(CallBase &CB) {
 
     // If we can constant fold this, mark the result of the call as a
     // constant.
-    if (Constant *C = ConstantFoldCall(&CB, F, Operands, &GetTLI(*F))) {
+    if (Constant *C = ConstantFoldCall(&CB, F, Operands, TLI)) {
       mergeInValue(ValueState[&CB], &CB, ValueLatticeElement::get(C));
       return;
     }

>From 446a4d06aec7622993e068363a2edaa233dc31d8 Mon Sep 17 00:00:00 2001
From: Nikita Popov <github at npopov.com>
Date: Tue, 8 Sep 2026 10:00:26 +0200
Subject: [PATCH 2/2] Apply suggestion from antoniofrighetto

Co-authored-by: Antonio Frighetto <me at antoniofrighetto.com>
---
 llvm/lib/Analysis/ConstantFolding.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index 33be1d5bc9e39..4bcb91721ecac 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -2120,7 +2120,7 @@ bool llvm::canConstantFoldCallTo(const CallBase *Call, const Function *F,
   if (F->getIntrinsicID() != Intrinsic::not_intrinsic)
     return canConstantFoldIntrinsic(F->getIntrinsicID(), Call->isStrictFP());
 
-  if (!TLI || !F->hasName() || Call->isStrictFP())
+  if (!TLI || Call->isStrictFP())
     return false;
 
   LibFunc Func = TLI->getLibFunc(*F);



More information about the llvm-commits mailing list