Skip to content

[IRBuilder] (Target|InstSimplify)-fold intrinsics#204967

Open
artagnon wants to merge 4 commits into
llvm:mainfrom
artagnon:irbuilder-fold-intr
Open

[IRBuilder] (Target|InstSimplify)-fold intrinsics#204967
artagnon wants to merge 4 commits into
llvm:mainfrom
artagnon:irbuilder-fold-intr

Conversation

@artagnon

Copy link
Copy Markdown
Contributor

Includes changes to guard against a nullptr TLI and Call. TargetFold or InstSimplify fold in IRBuilderBase::CreateIntrinsic, in the same way we fold in Create(Unary|Binary)Intrinsic.

Includes changes to guard against a nullptr TLI and Call. TargetFold or
InstSimplify fold in IRBuilderBase::CreateIntrinsic, in the same way we
fold in Create(Unary|Binary)Intrinsic.
@artagnon artagnon requested review from dtcxzyw, kparzysz and nikic June 21, 2026 09:04
@llvmorg-github-actions llvmorg-github-actions Bot added backend:RISC-V llvm:ir llvm:analysis Includes value tracking, cost tables and constant folding llvm:transforms llvm:vectorcombine Cost-based vector combine pass labels Jun 21, 2026
@llvmorg-github-actions

llvmorg-github-actions Bot commented Jun 21, 2026

Copy link
Copy Markdown

@llvm/pr-subscribers-vectorizers
@llvm/pr-subscribers-llvm-ir

@llvm/pr-subscribers-llvm-analysis

Author: Ramkumar Ramachandra (artagnon)

Changes

Includes changes to guard against a nullptr TLI and Call. TargetFold or InstSimplify fold in IRBuilderBase::CreateIntrinsic, in the same way we fold in Create(Unary|Binary)Intrinsic.


Patch is 38.40 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/204967.diff

18 Files Affected:

  • (modified) llvm/include/llvm/Analysis/ConstantFolding.h (+3)
  • (modified) llvm/include/llvm/Analysis/InstSimplifyFolder.h (+5)
  • (modified) llvm/include/llvm/Analysis/TargetFolder.h (+10)
  • (modified) llvm/include/llvm/IR/ConstantFolder.h (+6)
  • (modified) llvm/include/llvm/IR/IRBuilderFolder.h (+8-6)
  • (modified) llvm/include/llvm/IR/NoFolder.h (+5)
  • (modified) llvm/lib/Analysis/ConstantFolding.cpp (+73-67)
  • (modified) llvm/lib/IR/IRBuilder.cpp (+5-2)
  • (modified) llvm/test/Transforms/IRCE/correct-loop-info.ll (+15-18)
  • (modified) llvm/test/Transforms/IndVarSimplify/lcssa-preservation.ll (+1-2)
  • (modified) llvm/test/Transforms/LoopUnroll/runtime-loop-multiexit-dom-verify.ll (+1-2)
  • (modified) llvm/test/Transforms/LoopVectorize/X86/replicating-load-store-costs.ll (+2-14)
  • (modified) llvm/test/Transforms/LoopVectorize/trip-count-expansion-may-introduce-ub.ll (+1-2)
  • (modified) llvm/test/Transforms/LoopVectorize/version-mem-access.ll (+2-9)
  • (modified) llvm/test/Transforms/VectorCombine/AArch64/shuffle-of-intrinsics.ll (+2-6)
  • (modified) llvm/test/Transforms/VectorCombine/AArch64/shuffletoidentity.ll (+4-8)
  • (modified) llvm/test/Transforms/VectorCombine/RISCV/shuffle-of-intrinsics.ll (+1-3)
  • (modified) llvm/test/Transforms/VectorCombine/X86/shuffle-of-intrinsics.ll (+3-8)
diff --git a/llvm/include/llvm/Analysis/ConstantFolding.h b/llvm/include/llvm/Analysis/ConstantFolding.h
index 2f7a327e1652a..f223ebf0586f4 100644
--- a/llvm/include/llvm/Analysis/ConstantFolding.h
+++ b/llvm/include/llvm/Analysis/ConstantFolding.h
@@ -175,6 +175,9 @@ LLVM_ABI Constant *ConstantFoldUnaryIntrinsic(Intrinsic::ID ID, Constant *Op,
 LLVM_ABI Constant *ConstantFoldBinaryIntrinsic(Intrinsic::ID ID, Constant *LHS,
                                                Constant *RHS, Type *Ty);
 
+LLVM_ABI Constant *ConstantFoldIntrinsic(Intrinsic::ID ID,
+                                         ArrayRef<Constant *> Ops, Type *Ty);
+
 /// ConstantFoldLoadThroughBitcast - try to cast constant to destination type
 /// returning null if unsuccessful. Can cast pointer to pointer or pointer to
 /// integer and vice versa if their sizes are equal.
diff --git a/llvm/include/llvm/Analysis/InstSimplifyFolder.h b/llvm/include/llvm/Analysis/InstSimplifyFolder.h
index a8dff839de214..93cf827750528 100644
--- a/llvm/include/llvm/Analysis/InstSimplifyFolder.h
+++ b/llvm/include/llvm/Analysis/InstSimplifyFolder.h
@@ -132,6 +132,11 @@ class LLVM_ABI InstSimplifyFolder final : public IRBuilderFolder {
     return simplifyBinaryIntrinsic(ID, Ty, LHS, RHS, FMF, SQ);
   }
 
+  Value *FoldIntrinsic(Intrinsic::ID ID, ArrayRef<Value *> Ops, Type *Ty,
+                       FastMathFlags FMF) const override {
+    return simplifyIntrinsic(ID, Ty, Ops, FMF, SQ);
+  }
+
   //===--------------------------------------------------------------------===//
   // Cast/Conversion Operators
   //===--------------------------------------------------------------------===//
diff --git a/llvm/include/llvm/Analysis/TargetFolder.h b/llvm/include/llvm/Analysis/TargetFolder.h
index 0a9d9c3d88111..74d8ac3f1fc1f 100644
--- a/llvm/include/llvm/Analysis/TargetFolder.h
+++ b/llvm/include/llvm/Analysis/TargetFolder.h
@@ -19,6 +19,7 @@
 #define LLVM_ANALYSIS_TARGETFOLDER_H
 
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVectorExtras.h"
 #include "llvm/Analysis/ConstantFolding.h"
 #include "llvm/IR/ConstantFold.h"
 #include "llvm/IR/Constants.h"
@@ -207,6 +208,15 @@ class LLVM_ABI TargetFolder final : public IRBuilderFolder {
     return nullptr;
   }
 
+  Value *FoldIntrinsic(Intrinsic::ID ID, ArrayRef<Value *> Ops, Type *Ty,
+                       FastMathFlags FMF) const override {
+    auto COps =
+        map_to_vector(Ops, [](Value *Op) { return dyn_cast<Constant>(Op); });
+    if (none_of(COps, equal_to(nullptr)))
+      return ConstantFoldIntrinsic(ID, COps, Ty);
+    return nullptr;
+  }
+
   //===--------------------------------------------------------------------===//
   // Cast/Conversion Operators
   //===--------------------------------------------------------------------===//
diff --git a/llvm/include/llvm/IR/ConstantFolder.h b/llvm/include/llvm/IR/ConstantFolder.h
index b2937c2339ca7..c47a990650f68 100644
--- a/llvm/include/llvm/IR/ConstantFolder.h
+++ b/llvm/include/llvm/IR/ConstantFolder.h
@@ -194,6 +194,12 @@ class LLVM_ABI ConstantFolder final : public IRBuilderFolder {
     return nullptr;
   }
 
+  Value *FoldIntrinsic(Intrinsic::ID ID, ArrayRef<Value *> Ops, Type *Ty,
+                       FastMathFlags FMF) const override {
+    // Use TargetFolder or InstSimplifyFolder instead.
+    return nullptr;
+  }
+
   //===--------------------------------------------------------------------===//
   // Cast/Conversion Operators
   //===--------------------------------------------------------------------===//
diff --git a/llvm/include/llvm/IR/IRBuilderFolder.h b/llvm/include/llvm/IR/IRBuilderFolder.h
index e68fff29b7165..1699845b22466 100644
--- a/llvm/include/llvm/IR/IRBuilderFolder.h
+++ b/llvm/include/llvm/IR/IRBuilderFolder.h
@@ -76,13 +76,15 @@ class LLVM_ABI IRBuilderFolder {
   virtual Value *FoldCast(Instruction::CastOps Op, Value *V,
                           Type *DestTy) const = 0;
 
-  virtual Value *
-  FoldUnaryIntrinsic(Intrinsic::ID ID, Value *Op, Type *Ty,
-                     FastMathFlags FMF = FastMathFlags()) const = 0;
+  virtual Value *FoldUnaryIntrinsic(Intrinsic::ID ID, Value *Op, Type *Ty,
+                                    FastMathFlags FMF = {}) const = 0;
 
-  virtual Value *
-  FoldBinaryIntrinsic(Intrinsic::ID ID, Value *LHS, Value *RHS, Type *Ty,
-                      FastMathFlags FMF = FastMathFlags()) const = 0;
+  virtual Value *FoldBinaryIntrinsic(Intrinsic::ID ID, Value *LHS, Value *RHS,
+                                     Type *Ty,
+                                     FastMathFlags FMF = {}) const = 0;
+
+  virtual Value *FoldIntrinsic(Intrinsic::ID ID, ArrayRef<Value *> Ops,
+                               Type *Ty, FastMathFlags FMF = {}) const = 0;
 
   //===--------------------------------------------------------------------===//
   // Cast/Conversion Operators
diff --git a/llvm/include/llvm/IR/NoFolder.h b/llvm/include/llvm/IR/NoFolder.h
index a86cbf724e69f..5ba098d5cddf4 100644
--- a/llvm/include/llvm/IR/NoFolder.h
+++ b/llvm/include/llvm/IR/NoFolder.h
@@ -124,6 +124,11 @@ class LLVM_ABI NoFolder final : public IRBuilderFolder {
     return nullptr;
   }
 
+  Value *FoldIntrinsic(Intrinsic::ID ID, ArrayRef<Value *> Ops, Type *Ty,
+                       FastMathFlags FMF) const override {
+    return nullptr;
+  }
+
   //===--------------------------------------------------------------------===//
   // Cast/Conversion Operators
   //===--------------------------------------------------------------------===//
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index f18b7a0b66a21..aba7492de9179 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -2489,13 +2489,13 @@ getEvaluationRoundingMode(const ConstrainedFPIntrinsic *CI) {
 }
 
 /// Try to constant fold llvm.canonicalize for the given caller and value.
-static Constant *constantFoldCanonicalize(const Type *Ty, const CallBase *CI,
-                                          const APFloat &Src) {
+static Constant *constantFoldCanonicalize(const Type *Ty, const APFloat &Src,
+                                          const Function *CtxF = nullptr) {
   // Zero, positive and negative, is always OK to fold.
   if (Src.isZero()) {
     // Get a fresh 0, since ppc_fp128 does have non-canonical zeros.
     return ConstantFP::get(
-        CI->getContext(),
+        Ty->getContext(),
         APFloat::getZero(Src.getSemantics(), Src.isNegative()));
   }
 
@@ -2507,14 +2507,13 @@ static Constant *constantFoldCanonicalize(const Type *Ty, const CallBase *CI,
   // Denorms and nans may have special encodings, but it should be OK to fold a
   // totally average number.
   if (Src.isNormal() || Src.isInfinity())
-    return ConstantFP::get(CI->getContext(), Src);
+    return ConstantFP::get(Ty->getContext(), Src);
 
-  if (Src.isDenormal() && CI->getParent() && CI->getFunction()) {
-    DenormalMode DenormMode =
-        CI->getFunction()->getDenormalMode(Src.getSemantics());
+  if (Src.isDenormal() && CtxF) {
+    DenormalMode DenormMode = CtxF->getDenormalMode(Src.getSemantics());
 
     if (DenormMode == DenormalMode::getIEEE())
-      return ConstantFP::get(CI->getContext(), Src);
+      return ConstantFP::get(Ty->getContext(), Src);
 
     if (DenormMode.Input == DenormalMode::Dynamic)
       return nullptr;
@@ -2531,7 +2530,7 @@ static Constant *constantFoldCanonicalize(const Type *Ty, const CallBase *CI,
          (DenormMode.Output == DenormalMode::PositiveZero &&
           DenormMode.Input == DenormalMode::IEEE));
 
-    return ConstantFP::get(CI->getContext(),
+    return ConstantFP::get(Ty->getContext(),
                            APFloat::getZero(Src.getSemantics(), !IsPositive));
   }
 
@@ -2539,11 +2538,10 @@ static Constant *constantFoldCanonicalize(const Type *Ty, const CallBase *CI,
 }
 
 static Constant *ConstantFoldScalarCall1(StringRef Name,
-                                         Intrinsic::ID IntrinsicID,
-                                         Type *Ty,
+                                         Intrinsic::ID IntrinsicID, Type *Ty,
                                          ArrayRef<Constant *> Operands,
-                                         const TargetLibraryInfo *TLI,
-                                         const CallBase *Call) {
+                                         const TargetLibraryInfo *TLI = nullptr,
+                                         const CallBase *Call = nullptr) {
   assert(Operands.size() == 1 && "Wrong number of operands.");
 
   if (IntrinsicID == Intrinsic::is_constant) {
@@ -2580,7 +2578,7 @@ static Constant *ConstantFoldScalarCall1(StringRef Name,
       // a function during inlining), Call's caller may not be available.
       // So check Call's BB first before querying Call->getCaller.
       const Function *Caller =
-          Call->getParent() ? Call->getCaller() : nullptr;
+          Call && Call->getParent() ? Call->getCaller() : nullptr;
       if (Caller &&
           !NullPointerIsDefined(
               Caller, Operands[0]->getType()->getPointerAddressSpace())) {
@@ -2622,8 +2620,11 @@ static Constant *ConstantFoldScalarCall1(StringRef Name,
       return ConstantInt::get(Ty, Int);
     }
 
-    if (IntrinsicID == Intrinsic::canonicalize)
-      return constantFoldCanonicalize(Ty, Call, U);
+    if (IntrinsicID == Intrinsic::canonicalize) {
+      const Function *CtxF =
+          Call && Call->getParent() ? Call->getFunction() : nullptr;
+      return constantFoldCanonicalize(Ty, U, CtxF);
+    }
 
 #if defined(HAS_IEE754_FLOAT128) && defined(HAS_LOGF128)
     if (Ty->isFP128Ty()) {
@@ -2697,48 +2698,49 @@ static Constant *ConstantFoldScalarCall1(StringRef Name,
     // Rounding operations (floor, trunc, ceil, round and nearbyint) do not
     // raise FP exceptions, unless the argument is signaling NaN.
 
-    std::optional<APFloat::roundingMode> RM;
-    switch (IntrinsicID) {
-    default:
-      break;
-    case Intrinsic::experimental_constrained_nearbyint:
-    case Intrinsic::experimental_constrained_rint: {
-      auto CI = cast<ConstrainedFPIntrinsic>(Call);
-      RM = CI->getRoundingMode();
-      if (!RM || *RM == RoundingMode::Dynamic)
-        return nullptr;
-      break;
-    }
-    case Intrinsic::experimental_constrained_round:
-      RM = APFloat::rmNearestTiesToAway;
-      break;
-    case Intrinsic::experimental_constrained_ceil:
-      RM = APFloat::rmTowardPositive;
-      break;
-    case Intrinsic::experimental_constrained_floor:
-      RM = APFloat::rmTowardNegative;
-      break;
-    case Intrinsic::experimental_constrained_trunc:
-      RM = APFloat::rmTowardZero;
-      break;
-    }
-    if (RM) {
-      auto CI = cast<ConstrainedFPIntrinsic>(Call);
-      if (U.isFinite()) {
-        APFloat::opStatus St = U.roundToIntegral(*RM);
-        if (IntrinsicID == Intrinsic::experimental_constrained_rint &&
-            St == APFloat::opInexact) {
+    if (auto *CI = dyn_cast_or_null<ConstrainedFPIntrinsic>(Call)) {
+      std::optional<APFloat::roundingMode> RM;
+      switch (IntrinsicID) {
+      default:
+        break;
+      case Intrinsic::experimental_constrained_nearbyint:
+      case Intrinsic::experimental_constrained_rint: {
+        RM = CI->getRoundingMode();
+        if (!RM || *RM == RoundingMode::Dynamic)
+          return nullptr;
+        break;
+      }
+      case Intrinsic::experimental_constrained_round:
+        RM = APFloat::rmNearestTiesToAway;
+        break;
+      case Intrinsic::experimental_constrained_ceil:
+        RM = APFloat::rmTowardPositive;
+        break;
+      case Intrinsic::experimental_constrained_floor:
+        RM = APFloat::rmTowardNegative;
+        break;
+      case Intrinsic::experimental_constrained_trunc:
+        RM = APFloat::rmTowardZero;
+        break;
+      }
+      if (RM) {
+        if (U.isFinite()) {
+          APFloat::opStatus St = U.roundToIntegral(*RM);
+          if (IntrinsicID == Intrinsic::experimental_constrained_rint &&
+              St == APFloat::opInexact) {
+            std::optional<fp::ExceptionBehavior> EB =
+                CI->getExceptionBehavior();
+            if (EB == fp::ebStrict)
+              return nullptr;
+          }
+        } else if (U.isSignaling()) {
           std::optional<fp::ExceptionBehavior> EB = CI->getExceptionBehavior();
-          if (EB == fp::ebStrict)
+          if (EB && *EB != fp::ebIgnore)
             return nullptr;
+          U = APFloat::getQNaN(U.getSemantics());
         }
-      } else if (U.isSignaling()) {
-        std::optional<fp::ExceptionBehavior> EB = CI->getExceptionBehavior();
-        if (EB && *EB != fp::ebIgnore)
-          return nullptr;
-        U = APFloat::getQNaN(U.getSemantics());
+        return ConstantFP::get(Ty, U);
       }
-      return ConstantFP::get(Ty, U);
     }
 
     // NVVM float/double to signed/unsigned int32/int64 conversions:
@@ -3350,7 +3352,7 @@ static Constant *ConstantFoldNextToward(const APFloat &Op0, const APFloat &Op1,
 
 static Constant *ConstantFoldLibCall2(StringRef Name, Type *Ty,
                                       ArrayRef<Constant *> Operands,
-                                      const TargetLibraryInfo *TLI) {
+                                      const TargetLibraryInfo *TLI = nullptr) {
   if (!TLI)
     return nullptr;
 
@@ -3421,7 +3423,7 @@ static Constant *ConstantFoldLibCall2(StringRef Name, Type *Ty,
 
 static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
                                             ArrayRef<Constant *> Operands,
-                                            const CallBase *Call) {
+                                            const CallBase *Call = nullptr) {
   assert(Operands.size() == 2 && "Wrong number of operands.");
 
   if (Ty->isFloatingPointTy()) {
@@ -4098,11 +4100,10 @@ static Constant *ConstantFoldAMDGCNPermIntrinsic(ArrayRef<Constant *> Operands,
 }
 
 static Constant *ConstantFoldScalarCall3(StringRef Name,
-                                         Intrinsic::ID IntrinsicID,
-                                         Type *Ty,
+                                         Intrinsic::ID IntrinsicID, Type *Ty,
                                          ArrayRef<Constant *> Operands,
-                                         const TargetLibraryInfo *TLI,
-                                         const CallBase *Call) {
+                                         const TargetLibraryInfo *TLI = nullptr,
+                                         const CallBase *Call = nullptr) {
   assert(Operands.size() == 3 && "Wrong number of operands.");
 
   if (const auto *Op1 = dyn_cast<ConstantFP>(Operands[0])) {
@@ -4112,7 +4113,8 @@ static Constant *ConstantFoldScalarCall3(StringRef Name,
         const APFloat &C2 = Op2->getValueAPF();
         const APFloat &C3 = Op3->getValueAPF();
 
-        if (const auto *ConstrIntr = dyn_cast<ConstrainedFPIntrinsic>(Call)) {
+        if (const auto *ConstrIntr =
+                dyn_cast_or_null<ConstrainedFPIntrinsic>(Call)) {
           RoundingMode RM = getEvaluationRoundingMode(ConstrIntr);
           APFloat Res = C1;
           APFloat::opStatus St;
@@ -4262,11 +4264,10 @@ static Constant *ConstantFoldScalarCall3(StringRef Name,
 }
 
 static Constant *ConstantFoldScalarCall(StringRef Name,
-                                        Intrinsic::ID IntrinsicID,
-                                        Type *Ty,
+                                        Intrinsic::ID IntrinsicID, Type *Ty,
                                         ArrayRef<Constant *> Operands,
-                                        const TargetLibraryInfo *TLI,
-                                        const CallBase *Call) {
+                                        const TargetLibraryInfo *TLI = nullptr,
+                                        const CallBase *Call = nullptr) {
   if (IntrinsicID != Intrinsic::not_intrinsic &&
       any_of(Operands, IsaPred<PoisonValue>) &&
       intrinsicPropagatesPoison(IntrinsicID))
@@ -4708,12 +4709,17 @@ ConstantFoldStructCall(StringRef Name, Intrinsic::ID IntrinsicID,
 
 Constant *llvm::ConstantFoldUnaryIntrinsic(Intrinsic::ID ID, Constant *Op,
                                            Type *Ty) {
-  return ConstantFoldScalarCall1("", ID, Ty, Op, nullptr, nullptr);
+  return ConstantFoldScalarCall1("", ID, Ty, Op);
 }
 
 Constant *llvm::ConstantFoldBinaryIntrinsic(Intrinsic::ID ID, Constant *LHS,
                                             Constant *RHS, Type *Ty) {
-  return ConstantFoldIntrinsicCall2(ID, Ty, {LHS, RHS}, nullptr);
+  return ConstantFoldIntrinsicCall2(ID, Ty, {LHS, RHS});
+}
+
+Constant *llvm::ConstantFoldIntrinsic(Intrinsic::ID ID,
+                                      ArrayRef<Constant *> Ops, Type *Ty) {
+  return ConstantFoldScalarCall("", ID, Ty, Ops);
 }
 
 Constant *llvm::ConstantFoldCall(const CallBase *Call, Function *F,
diff --git a/llvm/lib/IR/IRBuilder.cpp b/llvm/lib/IR/IRBuilder.cpp
index eac591af3f495..46a52d94ec7f4 100644
--- a/llvm/lib/IR/IRBuilder.cpp
+++ b/llvm/lib/IR/IRBuilder.cpp
@@ -966,7 +966,9 @@ Value *IRBuilderBase::CreateIntrinsic(Intrinsic::ID ID,
                                       FMFSource FMFSource, const Twine &Name,
                                       ArrayRef<OperandBundleDef> OpBundles,
                                       function_ref<void(CallInst *)> SetFn) {
-  // TODO: Try to constant-fold.
+  Type *RetTy = Intrinsic::getType(Context, ID, OverloadTypes)->getReturnType();
+  if (Value *V = Folder.FoldIntrinsic(ID, Args, RetTy, FMFSource.get(FMF)))
+    return V;
   CallInst *CI = CreateIntrinsicWithoutFolding(ID, OverloadTypes, Args,
                                                FMFSource, Name, OpBundles);
   SetFn(CI);
@@ -977,7 +979,8 @@ Value *IRBuilderBase::CreateIntrinsic(Type *RetTy, Intrinsic::ID ID,
                                       ArrayRef<Value *> Args,
                                       FMFSource FMFSource, const Twine &Name,
                                       function_ref<void(CallInst *)> SetFn) {
-  // TODO: Try to constant-fold.
+  if (Value *V = Folder.FoldIntrinsic(ID, Args, RetTy, FMFSource.get(FMF)))
+    return V;
   CallInst *CI =
       CreateIntrinsicWithoutFolding(RetTy, ID, Args, FMFSource, Name);
   SetFn(CI);
diff --git a/llvm/test/Transforms/IRCE/correct-loop-info.ll b/llvm/test/Transforms/IRCE/correct-loop-info.ll
index c75de167681ad..7ea445718289a 100644
--- a/llvm/test/Transforms/IRCE/correct-loop-info.ll
+++ b/llvm/test/Transforms/IRCE/correct-loop-info.ll
@@ -13,33 +13,30 @@ source_filename = "correct-loop-info.ll"
 define void @baz() personality ptr @ham {
 ; CHECK-LABEL: @baz(
 ; CHECK-NEXT:  bb:
-; CHECK-NEXT:    [[EXIT_PRELOOP_AT:%.*]] = call i32 @llvm.smax.i32(i32 undef, i32 -1)
-; CHECK-NEXT:    [[EXIT_MAINLOOP_AT:%.*]] = call i32 @llvm.smax.i32(i32 undef, i32 0)
 ; CHECK-NEXT:    br label [[OUTERHEADER:%.*]]
 ; CHECK:       outerheader:
 ; CHECK-NEXT:    [[TMP:%.*]] = icmp slt i32 undef, 84
 ; CHECK-NEXT:    br i1 [[TMP]], label [[BB2:%.*]], label [[BB16:%.*]]
 ; CHECK:       bb2:
-; CHECK-NEXT:    [[TMP0:%.*]] = icmp slt i32 undef, [[EXIT_PRELOOP_AT]]
-; CHECK-NEXT:    br i1 [[TMP0]], label [[INNERHEADER_PRELOOP_PREHEADER:%.*]], label [[PRELOOP_PSEUDO_EXIT:%.*]]
+; CHECK-NEXT:    br i1 false, label [[INNERHEADER_PRELOOP_PREHEADER:%.*]], label [[PRELOOP_PSEUDO_EXIT:%.*]]
 ; CHECK:       innerheader.preloop.preheader:
 ; CHECK-NEXT:    br label [[INNERHEADER_PRELOOP:%.*]]
 ; CHECK:       mainloop:
-; CHECK-NEXT:    [[TMP1:%.*]] = icmp slt i32 [[INDVAR_END:%.*]], [[EXIT_MAINLOOP_AT]]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp slt i32 [[INDVAR_END:%.*]], 2147483647
 ; CHECK-NEXT:    br i1 [[TMP1]], label [[INNERHEADER_PREHEADER:%.*]], label [[MAIN_PSEUDO_EXIT:%.*]]
 ; CHECK:       innerheader.preheader:
 ; CHECK-NEXT:    br label [[INNERHEADER:%.*]]
 ; CHECK:       innerheader:
 ; CHECK-NEXT:    [[TMP4:%.*]] = phi i32 [ [[TMP6:%.*]], [[BB8:%.*]] ], [ [[TMP4_PRELOOP_COPY:%.*]], [[INNERHEADER_PREHEADER]] ]
 ; CHECK-NEXT:    invoke void @pluto()
-; CHECK-NEXT:    to label [[BB5:%.*]] unwind label [[OUTER_EXITING_LOOPEXIT_SPLIT_LP_LOOPEXIT_SPLIT_LP:%.*]]
+; CHECK-NEXT:            to label [[BB5:%.*]] unwind label [[OUTER_EXITING_LOOPEXIT_SPLIT_LP_LOOPEXIT_SPLIT_LP:%.*]]
 ; CHECK:       bb5:
 ; CHECK-NEXT:    [[TMP6]] = add nsw i32 [[TMP4]], 1
 ; CH...
[truncated]

@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-llvm-transforms

Author: Ramkumar Ramachandra (artagnon)

Changes

Includes changes to guard against a nullptr TLI and Call. TargetFold or InstSimplify fold in IRBuilderBase::CreateIntrinsic, in the same way we fold in Create(Unary|Binary)Intrinsic.


Patch is 38.40 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/204967.diff

18 Files Affected:

  • (modified) llvm/include/llvm/Analysis/ConstantFolding.h (+3)
  • (modified) llvm/include/llvm/Analysis/InstSimplifyFolder.h (+5)
  • (modified) llvm/include/llvm/Analysis/TargetFolder.h (+10)
  • (modified) llvm/include/llvm/IR/ConstantFolder.h (+6)
  • (modified) llvm/include/llvm/IR/IRBuilderFolder.h (+8-6)
  • (modified) llvm/include/llvm/IR/NoFolder.h (+5)
  • (modified) llvm/lib/Analysis/ConstantFolding.cpp (+73-67)
  • (modified) llvm/lib/IR/IRBuilder.cpp (+5-2)
  • (modified) llvm/test/Transforms/IRCE/correct-loop-info.ll (+15-18)
  • (modified) llvm/test/Transforms/IndVarSimplify/lcssa-preservation.ll (+1-2)
  • (modified) llvm/test/Transforms/LoopUnroll/runtime-loop-multiexit-dom-verify.ll (+1-2)
  • (modified) llvm/test/Transforms/LoopVectorize/X86/replicating-load-store-costs.ll (+2-14)
  • (modified) llvm/test/Transforms/LoopVectorize/trip-count-expansion-may-introduce-ub.ll (+1-2)
  • (modified) llvm/test/Transforms/LoopVectorize/version-mem-access.ll (+2-9)
  • (modified) llvm/test/Transforms/VectorCombine/AArch64/shuffle-of-intrinsics.ll (+2-6)
  • (modified) llvm/test/Transforms/VectorCombine/AArch64/shuffletoidentity.ll (+4-8)
  • (modified) llvm/test/Transforms/VectorCombine/RISCV/shuffle-of-intrinsics.ll (+1-3)
  • (modified) llvm/test/Transforms/VectorCombine/X86/shuffle-of-intrinsics.ll (+3-8)
diff --git a/llvm/include/llvm/Analysis/ConstantFolding.h b/llvm/include/llvm/Analysis/ConstantFolding.h
index 2f7a327e1652a..f223ebf0586f4 100644
--- a/llvm/include/llvm/Analysis/ConstantFolding.h
+++ b/llvm/include/llvm/Analysis/ConstantFolding.h
@@ -175,6 +175,9 @@ LLVM_ABI Constant *ConstantFoldUnaryIntrinsic(Intrinsic::ID ID, Constant *Op,
 LLVM_ABI Constant *ConstantFoldBinaryIntrinsic(Intrinsic::ID ID, Constant *LHS,
                                                Constant *RHS, Type *Ty);
 
+LLVM_ABI Constant *ConstantFoldIntrinsic(Intrinsic::ID ID,
+                                         ArrayRef<Constant *> Ops, Type *Ty);
+
 /// ConstantFoldLoadThroughBitcast - try to cast constant to destination type
 /// returning null if unsuccessful. Can cast pointer to pointer or pointer to
 /// integer and vice versa if their sizes are equal.
diff --git a/llvm/include/llvm/Analysis/InstSimplifyFolder.h b/llvm/include/llvm/Analysis/InstSimplifyFolder.h
index a8dff839de214..93cf827750528 100644
--- a/llvm/include/llvm/Analysis/InstSimplifyFolder.h
+++ b/llvm/include/llvm/Analysis/InstSimplifyFolder.h
@@ -132,6 +132,11 @@ class LLVM_ABI InstSimplifyFolder final : public IRBuilderFolder {
     return simplifyBinaryIntrinsic(ID, Ty, LHS, RHS, FMF, SQ);
   }
 
+  Value *FoldIntrinsic(Intrinsic::ID ID, ArrayRef<Value *> Ops, Type *Ty,
+                       FastMathFlags FMF) const override {
+    return simplifyIntrinsic(ID, Ty, Ops, FMF, SQ);
+  }
+
   //===--------------------------------------------------------------------===//
   // Cast/Conversion Operators
   //===--------------------------------------------------------------------===//
diff --git a/llvm/include/llvm/Analysis/TargetFolder.h b/llvm/include/llvm/Analysis/TargetFolder.h
index 0a9d9c3d88111..74d8ac3f1fc1f 100644
--- a/llvm/include/llvm/Analysis/TargetFolder.h
+++ b/llvm/include/llvm/Analysis/TargetFolder.h
@@ -19,6 +19,7 @@
 #define LLVM_ANALYSIS_TARGETFOLDER_H
 
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVectorExtras.h"
 #include "llvm/Analysis/ConstantFolding.h"
 #include "llvm/IR/ConstantFold.h"
 #include "llvm/IR/Constants.h"
@@ -207,6 +208,15 @@ class LLVM_ABI TargetFolder final : public IRBuilderFolder {
     return nullptr;
   }
 
+  Value *FoldIntrinsic(Intrinsic::ID ID, ArrayRef<Value *> Ops, Type *Ty,
+                       FastMathFlags FMF) const override {
+    auto COps =
+        map_to_vector(Ops, [](Value *Op) { return dyn_cast<Constant>(Op); });
+    if (none_of(COps, equal_to(nullptr)))
+      return ConstantFoldIntrinsic(ID, COps, Ty);
+    return nullptr;
+  }
+
   //===--------------------------------------------------------------------===//
   // Cast/Conversion Operators
   //===--------------------------------------------------------------------===//
diff --git a/llvm/include/llvm/IR/ConstantFolder.h b/llvm/include/llvm/IR/ConstantFolder.h
index b2937c2339ca7..c47a990650f68 100644
--- a/llvm/include/llvm/IR/ConstantFolder.h
+++ b/llvm/include/llvm/IR/ConstantFolder.h
@@ -194,6 +194,12 @@ class LLVM_ABI ConstantFolder final : public IRBuilderFolder {
     return nullptr;
   }
 
+  Value *FoldIntrinsic(Intrinsic::ID ID, ArrayRef<Value *> Ops, Type *Ty,
+                       FastMathFlags FMF) const override {
+    // Use TargetFolder or InstSimplifyFolder instead.
+    return nullptr;
+  }
+
   //===--------------------------------------------------------------------===//
   // Cast/Conversion Operators
   //===--------------------------------------------------------------------===//
diff --git a/llvm/include/llvm/IR/IRBuilderFolder.h b/llvm/include/llvm/IR/IRBuilderFolder.h
index e68fff29b7165..1699845b22466 100644
--- a/llvm/include/llvm/IR/IRBuilderFolder.h
+++ b/llvm/include/llvm/IR/IRBuilderFolder.h
@@ -76,13 +76,15 @@ class LLVM_ABI IRBuilderFolder {
   virtual Value *FoldCast(Instruction::CastOps Op, Value *V,
                           Type *DestTy) const = 0;
 
-  virtual Value *
-  FoldUnaryIntrinsic(Intrinsic::ID ID, Value *Op, Type *Ty,
-                     FastMathFlags FMF = FastMathFlags()) const = 0;
+  virtual Value *FoldUnaryIntrinsic(Intrinsic::ID ID, Value *Op, Type *Ty,
+                                    FastMathFlags FMF = {}) const = 0;
 
-  virtual Value *
-  FoldBinaryIntrinsic(Intrinsic::ID ID, Value *LHS, Value *RHS, Type *Ty,
-                      FastMathFlags FMF = FastMathFlags()) const = 0;
+  virtual Value *FoldBinaryIntrinsic(Intrinsic::ID ID, Value *LHS, Value *RHS,
+                                     Type *Ty,
+                                     FastMathFlags FMF = {}) const = 0;
+
+  virtual Value *FoldIntrinsic(Intrinsic::ID ID, ArrayRef<Value *> Ops,
+                               Type *Ty, FastMathFlags FMF = {}) const = 0;
 
   //===--------------------------------------------------------------------===//
   // Cast/Conversion Operators
diff --git a/llvm/include/llvm/IR/NoFolder.h b/llvm/include/llvm/IR/NoFolder.h
index a86cbf724e69f..5ba098d5cddf4 100644
--- a/llvm/include/llvm/IR/NoFolder.h
+++ b/llvm/include/llvm/IR/NoFolder.h
@@ -124,6 +124,11 @@ class LLVM_ABI NoFolder final : public IRBuilderFolder {
     return nullptr;
   }
 
+  Value *FoldIntrinsic(Intrinsic::ID ID, ArrayRef<Value *> Ops, Type *Ty,
+                       FastMathFlags FMF) const override {
+    return nullptr;
+  }
+
   //===--------------------------------------------------------------------===//
   // Cast/Conversion Operators
   //===--------------------------------------------------------------------===//
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index f18b7a0b66a21..aba7492de9179 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -2489,13 +2489,13 @@ getEvaluationRoundingMode(const ConstrainedFPIntrinsic *CI) {
 }
 
 /// Try to constant fold llvm.canonicalize for the given caller and value.
-static Constant *constantFoldCanonicalize(const Type *Ty, const CallBase *CI,
-                                          const APFloat &Src) {
+static Constant *constantFoldCanonicalize(const Type *Ty, const APFloat &Src,
+                                          const Function *CtxF = nullptr) {
   // Zero, positive and negative, is always OK to fold.
   if (Src.isZero()) {
     // Get a fresh 0, since ppc_fp128 does have non-canonical zeros.
     return ConstantFP::get(
-        CI->getContext(),
+        Ty->getContext(),
         APFloat::getZero(Src.getSemantics(), Src.isNegative()));
   }
 
@@ -2507,14 +2507,13 @@ static Constant *constantFoldCanonicalize(const Type *Ty, const CallBase *CI,
   // Denorms and nans may have special encodings, but it should be OK to fold a
   // totally average number.
   if (Src.isNormal() || Src.isInfinity())
-    return ConstantFP::get(CI->getContext(), Src);
+    return ConstantFP::get(Ty->getContext(), Src);
 
-  if (Src.isDenormal() && CI->getParent() && CI->getFunction()) {
-    DenormalMode DenormMode =
-        CI->getFunction()->getDenormalMode(Src.getSemantics());
+  if (Src.isDenormal() && CtxF) {
+    DenormalMode DenormMode = CtxF->getDenormalMode(Src.getSemantics());
 
     if (DenormMode == DenormalMode::getIEEE())
-      return ConstantFP::get(CI->getContext(), Src);
+      return ConstantFP::get(Ty->getContext(), Src);
 
     if (DenormMode.Input == DenormalMode::Dynamic)
       return nullptr;
@@ -2531,7 +2530,7 @@ static Constant *constantFoldCanonicalize(const Type *Ty, const CallBase *CI,
          (DenormMode.Output == DenormalMode::PositiveZero &&
           DenormMode.Input == DenormalMode::IEEE));
 
-    return ConstantFP::get(CI->getContext(),
+    return ConstantFP::get(Ty->getContext(),
                            APFloat::getZero(Src.getSemantics(), !IsPositive));
   }
 
@@ -2539,11 +2538,10 @@ static Constant *constantFoldCanonicalize(const Type *Ty, const CallBase *CI,
 }
 
 static Constant *ConstantFoldScalarCall1(StringRef Name,
-                                         Intrinsic::ID IntrinsicID,
-                                         Type *Ty,
+                                         Intrinsic::ID IntrinsicID, Type *Ty,
                                          ArrayRef<Constant *> Operands,
-                                         const TargetLibraryInfo *TLI,
-                                         const CallBase *Call) {
+                                         const TargetLibraryInfo *TLI = nullptr,
+                                         const CallBase *Call = nullptr) {
   assert(Operands.size() == 1 && "Wrong number of operands.");
 
   if (IntrinsicID == Intrinsic::is_constant) {
@@ -2580,7 +2578,7 @@ static Constant *ConstantFoldScalarCall1(StringRef Name,
       // a function during inlining), Call's caller may not be available.
       // So check Call's BB first before querying Call->getCaller.
       const Function *Caller =
-          Call->getParent() ? Call->getCaller() : nullptr;
+          Call && Call->getParent() ? Call->getCaller() : nullptr;
       if (Caller &&
           !NullPointerIsDefined(
               Caller, Operands[0]->getType()->getPointerAddressSpace())) {
@@ -2622,8 +2620,11 @@ static Constant *ConstantFoldScalarCall1(StringRef Name,
       return ConstantInt::get(Ty, Int);
     }
 
-    if (IntrinsicID == Intrinsic::canonicalize)
-      return constantFoldCanonicalize(Ty, Call, U);
+    if (IntrinsicID == Intrinsic::canonicalize) {
+      const Function *CtxF =
+          Call && Call->getParent() ? Call->getFunction() : nullptr;
+      return constantFoldCanonicalize(Ty, U, CtxF);
+    }
 
 #if defined(HAS_IEE754_FLOAT128) && defined(HAS_LOGF128)
     if (Ty->isFP128Ty()) {
@@ -2697,48 +2698,49 @@ static Constant *ConstantFoldScalarCall1(StringRef Name,
     // Rounding operations (floor, trunc, ceil, round and nearbyint) do not
     // raise FP exceptions, unless the argument is signaling NaN.
 
-    std::optional<APFloat::roundingMode> RM;
-    switch (IntrinsicID) {
-    default:
-      break;
-    case Intrinsic::experimental_constrained_nearbyint:
-    case Intrinsic::experimental_constrained_rint: {
-      auto CI = cast<ConstrainedFPIntrinsic>(Call);
-      RM = CI->getRoundingMode();
-      if (!RM || *RM == RoundingMode::Dynamic)
-        return nullptr;
-      break;
-    }
-    case Intrinsic::experimental_constrained_round:
-      RM = APFloat::rmNearestTiesToAway;
-      break;
-    case Intrinsic::experimental_constrained_ceil:
-      RM = APFloat::rmTowardPositive;
-      break;
-    case Intrinsic::experimental_constrained_floor:
-      RM = APFloat::rmTowardNegative;
-      break;
-    case Intrinsic::experimental_constrained_trunc:
-      RM = APFloat::rmTowardZero;
-      break;
-    }
-    if (RM) {
-      auto CI = cast<ConstrainedFPIntrinsic>(Call);
-      if (U.isFinite()) {
-        APFloat::opStatus St = U.roundToIntegral(*RM);
-        if (IntrinsicID == Intrinsic::experimental_constrained_rint &&
-            St == APFloat::opInexact) {
+    if (auto *CI = dyn_cast_or_null<ConstrainedFPIntrinsic>(Call)) {
+      std::optional<APFloat::roundingMode> RM;
+      switch (IntrinsicID) {
+      default:
+        break;
+      case Intrinsic::experimental_constrained_nearbyint:
+      case Intrinsic::experimental_constrained_rint: {
+        RM = CI->getRoundingMode();
+        if (!RM || *RM == RoundingMode::Dynamic)
+          return nullptr;
+        break;
+      }
+      case Intrinsic::experimental_constrained_round:
+        RM = APFloat::rmNearestTiesToAway;
+        break;
+      case Intrinsic::experimental_constrained_ceil:
+        RM = APFloat::rmTowardPositive;
+        break;
+      case Intrinsic::experimental_constrained_floor:
+        RM = APFloat::rmTowardNegative;
+        break;
+      case Intrinsic::experimental_constrained_trunc:
+        RM = APFloat::rmTowardZero;
+        break;
+      }
+      if (RM) {
+        if (U.isFinite()) {
+          APFloat::opStatus St = U.roundToIntegral(*RM);
+          if (IntrinsicID == Intrinsic::experimental_constrained_rint &&
+              St == APFloat::opInexact) {
+            std::optional<fp::ExceptionBehavior> EB =
+                CI->getExceptionBehavior();
+            if (EB == fp::ebStrict)
+              return nullptr;
+          }
+        } else if (U.isSignaling()) {
           std::optional<fp::ExceptionBehavior> EB = CI->getExceptionBehavior();
-          if (EB == fp::ebStrict)
+          if (EB && *EB != fp::ebIgnore)
             return nullptr;
+          U = APFloat::getQNaN(U.getSemantics());
         }
-      } else if (U.isSignaling()) {
-        std::optional<fp::ExceptionBehavior> EB = CI->getExceptionBehavior();
-        if (EB && *EB != fp::ebIgnore)
-          return nullptr;
-        U = APFloat::getQNaN(U.getSemantics());
+        return ConstantFP::get(Ty, U);
       }
-      return ConstantFP::get(Ty, U);
     }
 
     // NVVM float/double to signed/unsigned int32/int64 conversions:
@@ -3350,7 +3352,7 @@ static Constant *ConstantFoldNextToward(const APFloat &Op0, const APFloat &Op1,
 
 static Constant *ConstantFoldLibCall2(StringRef Name, Type *Ty,
                                       ArrayRef<Constant *> Operands,
-                                      const TargetLibraryInfo *TLI) {
+                                      const TargetLibraryInfo *TLI = nullptr) {
   if (!TLI)
     return nullptr;
 
@@ -3421,7 +3423,7 @@ static Constant *ConstantFoldLibCall2(StringRef Name, Type *Ty,
 
 static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
                                             ArrayRef<Constant *> Operands,
-                                            const CallBase *Call) {
+                                            const CallBase *Call = nullptr) {
   assert(Operands.size() == 2 && "Wrong number of operands.");
 
   if (Ty->isFloatingPointTy()) {
@@ -4098,11 +4100,10 @@ static Constant *ConstantFoldAMDGCNPermIntrinsic(ArrayRef<Constant *> Operands,
 }
 
 static Constant *ConstantFoldScalarCall3(StringRef Name,
-                                         Intrinsic::ID IntrinsicID,
-                                         Type *Ty,
+                                         Intrinsic::ID IntrinsicID, Type *Ty,
                                          ArrayRef<Constant *> Operands,
-                                         const TargetLibraryInfo *TLI,
-                                         const CallBase *Call) {
+                                         const TargetLibraryInfo *TLI = nullptr,
+                                         const CallBase *Call = nullptr) {
   assert(Operands.size() == 3 && "Wrong number of operands.");
 
   if (const auto *Op1 = dyn_cast<ConstantFP>(Operands[0])) {
@@ -4112,7 +4113,8 @@ static Constant *ConstantFoldScalarCall3(StringRef Name,
         const APFloat &C2 = Op2->getValueAPF();
         const APFloat &C3 = Op3->getValueAPF();
 
-        if (const auto *ConstrIntr = dyn_cast<ConstrainedFPIntrinsic>(Call)) {
+        if (const auto *ConstrIntr =
+                dyn_cast_or_null<ConstrainedFPIntrinsic>(Call)) {
           RoundingMode RM = getEvaluationRoundingMode(ConstrIntr);
           APFloat Res = C1;
           APFloat::opStatus St;
@@ -4262,11 +4264,10 @@ static Constant *ConstantFoldScalarCall3(StringRef Name,
 }
 
 static Constant *ConstantFoldScalarCall(StringRef Name,
-                                        Intrinsic::ID IntrinsicID,
-                                        Type *Ty,
+                                        Intrinsic::ID IntrinsicID, Type *Ty,
                                         ArrayRef<Constant *> Operands,
-                                        const TargetLibraryInfo *TLI,
-                                        const CallBase *Call) {
+                                        const TargetLibraryInfo *TLI = nullptr,
+                                        const CallBase *Call = nullptr) {
   if (IntrinsicID != Intrinsic::not_intrinsic &&
       any_of(Operands, IsaPred<PoisonValue>) &&
       intrinsicPropagatesPoison(IntrinsicID))
@@ -4708,12 +4709,17 @@ ConstantFoldStructCall(StringRef Name, Intrinsic::ID IntrinsicID,
 
 Constant *llvm::ConstantFoldUnaryIntrinsic(Intrinsic::ID ID, Constant *Op,
                                            Type *Ty) {
-  return ConstantFoldScalarCall1("", ID, Ty, Op, nullptr, nullptr);
+  return ConstantFoldScalarCall1("", ID, Ty, Op);
 }
 
 Constant *llvm::ConstantFoldBinaryIntrinsic(Intrinsic::ID ID, Constant *LHS,
                                             Constant *RHS, Type *Ty) {
-  return ConstantFoldIntrinsicCall2(ID, Ty, {LHS, RHS}, nullptr);
+  return ConstantFoldIntrinsicCall2(ID, Ty, {LHS, RHS});
+}
+
+Constant *llvm::ConstantFoldIntrinsic(Intrinsic::ID ID,
+                                      ArrayRef<Constant *> Ops, Type *Ty) {
+  return ConstantFoldScalarCall("", ID, Ty, Ops);
 }
 
 Constant *llvm::ConstantFoldCall(const CallBase *Call, Function *F,
diff --git a/llvm/lib/IR/IRBuilder.cpp b/llvm/lib/IR/IRBuilder.cpp
index eac591af3f495..46a52d94ec7f4 100644
--- a/llvm/lib/IR/IRBuilder.cpp
+++ b/llvm/lib/IR/IRBuilder.cpp
@@ -966,7 +966,9 @@ Value *IRBuilderBase::CreateIntrinsic(Intrinsic::ID ID,
                                       FMFSource FMFSource, const Twine &Name,
                                       ArrayRef<OperandBundleDef> OpBundles,
                                       function_ref<void(CallInst *)> SetFn) {
-  // TODO: Try to constant-fold.
+  Type *RetTy = Intrinsic::getType(Context, ID, OverloadTypes)->getReturnType();
+  if (Value *V = Folder.FoldIntrinsic(ID, Args, RetTy, FMFSource.get(FMF)))
+    return V;
   CallInst *CI = CreateIntrinsicWithoutFolding(ID, OverloadTypes, Args,
                                                FMFSource, Name, OpBundles);
   SetFn(CI);
@@ -977,7 +979,8 @@ Value *IRBuilderBase::CreateIntrinsic(Type *RetTy, Intrinsic::ID ID,
                                       ArrayRef<Value *> Args,
                                       FMFSource FMFSource, const Twine &Name,
                                       function_ref<void(CallInst *)> SetFn) {
-  // TODO: Try to constant-fold.
+  if (Value *V = Folder.FoldIntrinsic(ID, Args, RetTy, FMFSource.get(FMF)))
+    return V;
   CallInst *CI =
       CreateIntrinsicWithoutFolding(RetTy, ID, Args, FMFSource, Name);
   SetFn(CI);
diff --git a/llvm/test/Transforms/IRCE/correct-loop-info.ll b/llvm/test/Transforms/IRCE/correct-loop-info.ll
index c75de167681ad..7ea445718289a 100644
--- a/llvm/test/Transforms/IRCE/correct-loop-info.ll
+++ b/llvm/test/Transforms/IRCE/correct-loop-info.ll
@@ -13,33 +13,30 @@ source_filename = "correct-loop-info.ll"
 define void @baz() personality ptr @ham {
 ; CHECK-LABEL: @baz(
 ; CHECK-NEXT:  bb:
-; CHECK-NEXT:    [[EXIT_PRELOOP_AT:%.*]] = call i32 @llvm.smax.i32(i32 undef, i32 -1)
-; CHECK-NEXT:    [[EXIT_MAINLOOP_AT:%.*]] = call i32 @llvm.smax.i32(i32 undef, i32 0)
 ; CHECK-NEXT:    br label [[OUTERHEADER:%.*]]
 ; CHECK:       outerheader:
 ; CHECK-NEXT:    [[TMP:%.*]] = icmp slt i32 undef, 84
 ; CHECK-NEXT:    br i1 [[TMP]], label [[BB2:%.*]], label [[BB16:%.*]]
 ; CHECK:       bb2:
-; CHECK-NEXT:    [[TMP0:%.*]] = icmp slt i32 undef, [[EXIT_PRELOOP_AT]]
-; CHECK-NEXT:    br i1 [[TMP0]], label [[INNERHEADER_PRELOOP_PREHEADER:%.*]], label [[PRELOOP_PSEUDO_EXIT:%.*]]
+; CHECK-NEXT:    br i1 false, label [[INNERHEADER_PRELOOP_PREHEADER:%.*]], label [[PRELOOP_PSEUDO_EXIT:%.*]]
 ; CHECK:       innerheader.preloop.preheader:
 ; CHECK-NEXT:    br label [[INNERHEADER_PRELOOP:%.*]]
 ; CHECK:       mainloop:
-; CHECK-NEXT:    [[TMP1:%.*]] = icmp slt i32 [[INDVAR_END:%.*]], [[EXIT_MAINLOOP_AT]]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp slt i32 [[INDVAR_END:%.*]], 2147483647
 ; CHECK-NEXT:    br i1 [[TMP1]], label [[INNERHEADER_PREHEADER:%.*]], label [[MAIN_PSEUDO_EXIT:%.*]]
 ; CHECK:       innerheader.preheader:
 ; CHECK-NEXT:    br label [[INNERHEADER:%.*]]
 ; CHECK:       innerheader:
 ; CHECK-NEXT:    [[TMP4:%.*]] = phi i32 [ [[TMP6:%.*]], [[BB8:%.*]] ], [ [[TMP4_PRELOOP_COPY:%.*]], [[INNERHEADER_PREHEADER]] ]
 ; CHECK-NEXT:    invoke void @pluto()
-; CHECK-NEXT:    to label [[BB5:%.*]] unwind label [[OUTER_EXITING_LOOPEXIT_SPLIT_LP_LOOPEXIT_SPLIT_LP:%.*]]
+; CHECK-NEXT:            to label [[BB5:%.*]] unwind label [[OUTER_EXITING_LOOPEXIT_SPLIT_LP_LOOPEXIT_SPLIT_LP:%.*]]
 ; CHECK:       bb5:
 ; CHECK-NEXT:    [[TMP6]] = add nsw i32 [[TMP4]], 1
 ; CH...
[truncated]

@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-backend-risc-v

Author: Ramkumar Ramachandra (artagnon)

Changes

Includes changes to guard against a nullptr TLI and Call. TargetFold or InstSimplify fold in IRBuilderBase::CreateIntrinsic, in the same way we fold in Create(Unary|Binary)Intrinsic.


Patch is 38.40 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/204967.diff

18 Files Affected:

  • (modified) llvm/include/llvm/Analysis/ConstantFolding.h (+3)
  • (modified) llvm/include/llvm/Analysis/InstSimplifyFolder.h (+5)
  • (modified) llvm/include/llvm/Analysis/TargetFolder.h (+10)
  • (modified) llvm/include/llvm/IR/ConstantFolder.h (+6)
  • (modified) llvm/include/llvm/IR/IRBuilderFolder.h (+8-6)
  • (modified) llvm/include/llvm/IR/NoFolder.h (+5)
  • (modified) llvm/lib/Analysis/ConstantFolding.cpp (+73-67)
  • (modified) llvm/lib/IR/IRBuilder.cpp (+5-2)
  • (modified) llvm/test/Transforms/IRCE/correct-loop-info.ll (+15-18)
  • (modified) llvm/test/Transforms/IndVarSimplify/lcssa-preservation.ll (+1-2)
  • (modified) llvm/test/Transforms/LoopUnroll/runtime-loop-multiexit-dom-verify.ll (+1-2)
  • (modified) llvm/test/Transforms/LoopVectorize/X86/replicating-load-store-costs.ll (+2-14)
  • (modified) llvm/test/Transforms/LoopVectorize/trip-count-expansion-may-introduce-ub.ll (+1-2)
  • (modified) llvm/test/Transforms/LoopVectorize/version-mem-access.ll (+2-9)
  • (modified) llvm/test/Transforms/VectorCombine/AArch64/shuffle-of-intrinsics.ll (+2-6)
  • (modified) llvm/test/Transforms/VectorCombine/AArch64/shuffletoidentity.ll (+4-8)
  • (modified) llvm/test/Transforms/VectorCombine/RISCV/shuffle-of-intrinsics.ll (+1-3)
  • (modified) llvm/test/Transforms/VectorCombine/X86/shuffle-of-intrinsics.ll (+3-8)
diff --git a/llvm/include/llvm/Analysis/ConstantFolding.h b/llvm/include/llvm/Analysis/ConstantFolding.h
index 2f7a327e1652a..f223ebf0586f4 100644
--- a/llvm/include/llvm/Analysis/ConstantFolding.h
+++ b/llvm/include/llvm/Analysis/ConstantFolding.h
@@ -175,6 +175,9 @@ LLVM_ABI Constant *ConstantFoldUnaryIntrinsic(Intrinsic::ID ID, Constant *Op,
 LLVM_ABI Constant *ConstantFoldBinaryIntrinsic(Intrinsic::ID ID, Constant *LHS,
                                                Constant *RHS, Type *Ty);
 
+LLVM_ABI Constant *ConstantFoldIntrinsic(Intrinsic::ID ID,
+                                         ArrayRef<Constant *> Ops, Type *Ty);
+
 /// ConstantFoldLoadThroughBitcast - try to cast constant to destination type
 /// returning null if unsuccessful. Can cast pointer to pointer or pointer to
 /// integer and vice versa if their sizes are equal.
diff --git a/llvm/include/llvm/Analysis/InstSimplifyFolder.h b/llvm/include/llvm/Analysis/InstSimplifyFolder.h
index a8dff839de214..93cf827750528 100644
--- a/llvm/include/llvm/Analysis/InstSimplifyFolder.h
+++ b/llvm/include/llvm/Analysis/InstSimplifyFolder.h
@@ -132,6 +132,11 @@ class LLVM_ABI InstSimplifyFolder final : public IRBuilderFolder {
     return simplifyBinaryIntrinsic(ID, Ty, LHS, RHS, FMF, SQ);
   }
 
+  Value *FoldIntrinsic(Intrinsic::ID ID, ArrayRef<Value *> Ops, Type *Ty,
+                       FastMathFlags FMF) const override {
+    return simplifyIntrinsic(ID, Ty, Ops, FMF, SQ);
+  }
+
   //===--------------------------------------------------------------------===//
   // Cast/Conversion Operators
   //===--------------------------------------------------------------------===//
diff --git a/llvm/include/llvm/Analysis/TargetFolder.h b/llvm/include/llvm/Analysis/TargetFolder.h
index 0a9d9c3d88111..74d8ac3f1fc1f 100644
--- a/llvm/include/llvm/Analysis/TargetFolder.h
+++ b/llvm/include/llvm/Analysis/TargetFolder.h
@@ -19,6 +19,7 @@
 #define LLVM_ANALYSIS_TARGETFOLDER_H
 
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVectorExtras.h"
 #include "llvm/Analysis/ConstantFolding.h"
 #include "llvm/IR/ConstantFold.h"
 #include "llvm/IR/Constants.h"
@@ -207,6 +208,15 @@ class LLVM_ABI TargetFolder final : public IRBuilderFolder {
     return nullptr;
   }
 
+  Value *FoldIntrinsic(Intrinsic::ID ID, ArrayRef<Value *> Ops, Type *Ty,
+                       FastMathFlags FMF) const override {
+    auto COps =
+        map_to_vector(Ops, [](Value *Op) { return dyn_cast<Constant>(Op); });
+    if (none_of(COps, equal_to(nullptr)))
+      return ConstantFoldIntrinsic(ID, COps, Ty);
+    return nullptr;
+  }
+
   //===--------------------------------------------------------------------===//
   // Cast/Conversion Operators
   //===--------------------------------------------------------------------===//
diff --git a/llvm/include/llvm/IR/ConstantFolder.h b/llvm/include/llvm/IR/ConstantFolder.h
index b2937c2339ca7..c47a990650f68 100644
--- a/llvm/include/llvm/IR/ConstantFolder.h
+++ b/llvm/include/llvm/IR/ConstantFolder.h
@@ -194,6 +194,12 @@ class LLVM_ABI ConstantFolder final : public IRBuilderFolder {
     return nullptr;
   }
 
+  Value *FoldIntrinsic(Intrinsic::ID ID, ArrayRef<Value *> Ops, Type *Ty,
+                       FastMathFlags FMF) const override {
+    // Use TargetFolder or InstSimplifyFolder instead.
+    return nullptr;
+  }
+
   //===--------------------------------------------------------------------===//
   // Cast/Conversion Operators
   //===--------------------------------------------------------------------===//
diff --git a/llvm/include/llvm/IR/IRBuilderFolder.h b/llvm/include/llvm/IR/IRBuilderFolder.h
index e68fff29b7165..1699845b22466 100644
--- a/llvm/include/llvm/IR/IRBuilderFolder.h
+++ b/llvm/include/llvm/IR/IRBuilderFolder.h
@@ -76,13 +76,15 @@ class LLVM_ABI IRBuilderFolder {
   virtual Value *FoldCast(Instruction::CastOps Op, Value *V,
                           Type *DestTy) const = 0;
 
-  virtual Value *
-  FoldUnaryIntrinsic(Intrinsic::ID ID, Value *Op, Type *Ty,
-                     FastMathFlags FMF = FastMathFlags()) const = 0;
+  virtual Value *FoldUnaryIntrinsic(Intrinsic::ID ID, Value *Op, Type *Ty,
+                                    FastMathFlags FMF = {}) const = 0;
 
-  virtual Value *
-  FoldBinaryIntrinsic(Intrinsic::ID ID, Value *LHS, Value *RHS, Type *Ty,
-                      FastMathFlags FMF = FastMathFlags()) const = 0;
+  virtual Value *FoldBinaryIntrinsic(Intrinsic::ID ID, Value *LHS, Value *RHS,
+                                     Type *Ty,
+                                     FastMathFlags FMF = {}) const = 0;
+
+  virtual Value *FoldIntrinsic(Intrinsic::ID ID, ArrayRef<Value *> Ops,
+                               Type *Ty, FastMathFlags FMF = {}) const = 0;
 
   //===--------------------------------------------------------------------===//
   // Cast/Conversion Operators
diff --git a/llvm/include/llvm/IR/NoFolder.h b/llvm/include/llvm/IR/NoFolder.h
index a86cbf724e69f..5ba098d5cddf4 100644
--- a/llvm/include/llvm/IR/NoFolder.h
+++ b/llvm/include/llvm/IR/NoFolder.h
@@ -124,6 +124,11 @@ class LLVM_ABI NoFolder final : public IRBuilderFolder {
     return nullptr;
   }
 
+  Value *FoldIntrinsic(Intrinsic::ID ID, ArrayRef<Value *> Ops, Type *Ty,
+                       FastMathFlags FMF) const override {
+    return nullptr;
+  }
+
   //===--------------------------------------------------------------------===//
   // Cast/Conversion Operators
   //===--------------------------------------------------------------------===//
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index f18b7a0b66a21..aba7492de9179 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -2489,13 +2489,13 @@ getEvaluationRoundingMode(const ConstrainedFPIntrinsic *CI) {
 }
 
 /// Try to constant fold llvm.canonicalize for the given caller and value.
-static Constant *constantFoldCanonicalize(const Type *Ty, const CallBase *CI,
-                                          const APFloat &Src) {
+static Constant *constantFoldCanonicalize(const Type *Ty, const APFloat &Src,
+                                          const Function *CtxF = nullptr) {
   // Zero, positive and negative, is always OK to fold.
   if (Src.isZero()) {
     // Get a fresh 0, since ppc_fp128 does have non-canonical zeros.
     return ConstantFP::get(
-        CI->getContext(),
+        Ty->getContext(),
         APFloat::getZero(Src.getSemantics(), Src.isNegative()));
   }
 
@@ -2507,14 +2507,13 @@ static Constant *constantFoldCanonicalize(const Type *Ty, const CallBase *CI,
   // Denorms and nans may have special encodings, but it should be OK to fold a
   // totally average number.
   if (Src.isNormal() || Src.isInfinity())
-    return ConstantFP::get(CI->getContext(), Src);
+    return ConstantFP::get(Ty->getContext(), Src);
 
-  if (Src.isDenormal() && CI->getParent() && CI->getFunction()) {
-    DenormalMode DenormMode =
-        CI->getFunction()->getDenormalMode(Src.getSemantics());
+  if (Src.isDenormal() && CtxF) {
+    DenormalMode DenormMode = CtxF->getDenormalMode(Src.getSemantics());
 
     if (DenormMode == DenormalMode::getIEEE())
-      return ConstantFP::get(CI->getContext(), Src);
+      return ConstantFP::get(Ty->getContext(), Src);
 
     if (DenormMode.Input == DenormalMode::Dynamic)
       return nullptr;
@@ -2531,7 +2530,7 @@ static Constant *constantFoldCanonicalize(const Type *Ty, const CallBase *CI,
          (DenormMode.Output == DenormalMode::PositiveZero &&
           DenormMode.Input == DenormalMode::IEEE));
 
-    return ConstantFP::get(CI->getContext(),
+    return ConstantFP::get(Ty->getContext(),
                            APFloat::getZero(Src.getSemantics(), !IsPositive));
   }
 
@@ -2539,11 +2538,10 @@ static Constant *constantFoldCanonicalize(const Type *Ty, const CallBase *CI,
 }
 
 static Constant *ConstantFoldScalarCall1(StringRef Name,
-                                         Intrinsic::ID IntrinsicID,
-                                         Type *Ty,
+                                         Intrinsic::ID IntrinsicID, Type *Ty,
                                          ArrayRef<Constant *> Operands,
-                                         const TargetLibraryInfo *TLI,
-                                         const CallBase *Call) {
+                                         const TargetLibraryInfo *TLI = nullptr,
+                                         const CallBase *Call = nullptr) {
   assert(Operands.size() == 1 && "Wrong number of operands.");
 
   if (IntrinsicID == Intrinsic::is_constant) {
@@ -2580,7 +2578,7 @@ static Constant *ConstantFoldScalarCall1(StringRef Name,
       // a function during inlining), Call's caller may not be available.
       // So check Call's BB first before querying Call->getCaller.
       const Function *Caller =
-          Call->getParent() ? Call->getCaller() : nullptr;
+          Call && Call->getParent() ? Call->getCaller() : nullptr;
       if (Caller &&
           !NullPointerIsDefined(
               Caller, Operands[0]->getType()->getPointerAddressSpace())) {
@@ -2622,8 +2620,11 @@ static Constant *ConstantFoldScalarCall1(StringRef Name,
       return ConstantInt::get(Ty, Int);
     }
 
-    if (IntrinsicID == Intrinsic::canonicalize)
-      return constantFoldCanonicalize(Ty, Call, U);
+    if (IntrinsicID == Intrinsic::canonicalize) {
+      const Function *CtxF =
+          Call && Call->getParent() ? Call->getFunction() : nullptr;
+      return constantFoldCanonicalize(Ty, U, CtxF);
+    }
 
 #if defined(HAS_IEE754_FLOAT128) && defined(HAS_LOGF128)
     if (Ty->isFP128Ty()) {
@@ -2697,48 +2698,49 @@ static Constant *ConstantFoldScalarCall1(StringRef Name,
     // Rounding operations (floor, trunc, ceil, round and nearbyint) do not
     // raise FP exceptions, unless the argument is signaling NaN.
 
-    std::optional<APFloat::roundingMode> RM;
-    switch (IntrinsicID) {
-    default:
-      break;
-    case Intrinsic::experimental_constrained_nearbyint:
-    case Intrinsic::experimental_constrained_rint: {
-      auto CI = cast<ConstrainedFPIntrinsic>(Call);
-      RM = CI->getRoundingMode();
-      if (!RM || *RM == RoundingMode::Dynamic)
-        return nullptr;
-      break;
-    }
-    case Intrinsic::experimental_constrained_round:
-      RM = APFloat::rmNearestTiesToAway;
-      break;
-    case Intrinsic::experimental_constrained_ceil:
-      RM = APFloat::rmTowardPositive;
-      break;
-    case Intrinsic::experimental_constrained_floor:
-      RM = APFloat::rmTowardNegative;
-      break;
-    case Intrinsic::experimental_constrained_trunc:
-      RM = APFloat::rmTowardZero;
-      break;
-    }
-    if (RM) {
-      auto CI = cast<ConstrainedFPIntrinsic>(Call);
-      if (U.isFinite()) {
-        APFloat::opStatus St = U.roundToIntegral(*RM);
-        if (IntrinsicID == Intrinsic::experimental_constrained_rint &&
-            St == APFloat::opInexact) {
+    if (auto *CI = dyn_cast_or_null<ConstrainedFPIntrinsic>(Call)) {
+      std::optional<APFloat::roundingMode> RM;
+      switch (IntrinsicID) {
+      default:
+        break;
+      case Intrinsic::experimental_constrained_nearbyint:
+      case Intrinsic::experimental_constrained_rint: {
+        RM = CI->getRoundingMode();
+        if (!RM || *RM == RoundingMode::Dynamic)
+          return nullptr;
+        break;
+      }
+      case Intrinsic::experimental_constrained_round:
+        RM = APFloat::rmNearestTiesToAway;
+        break;
+      case Intrinsic::experimental_constrained_ceil:
+        RM = APFloat::rmTowardPositive;
+        break;
+      case Intrinsic::experimental_constrained_floor:
+        RM = APFloat::rmTowardNegative;
+        break;
+      case Intrinsic::experimental_constrained_trunc:
+        RM = APFloat::rmTowardZero;
+        break;
+      }
+      if (RM) {
+        if (U.isFinite()) {
+          APFloat::opStatus St = U.roundToIntegral(*RM);
+          if (IntrinsicID == Intrinsic::experimental_constrained_rint &&
+              St == APFloat::opInexact) {
+            std::optional<fp::ExceptionBehavior> EB =
+                CI->getExceptionBehavior();
+            if (EB == fp::ebStrict)
+              return nullptr;
+          }
+        } else if (U.isSignaling()) {
           std::optional<fp::ExceptionBehavior> EB = CI->getExceptionBehavior();
-          if (EB == fp::ebStrict)
+          if (EB && *EB != fp::ebIgnore)
             return nullptr;
+          U = APFloat::getQNaN(U.getSemantics());
         }
-      } else if (U.isSignaling()) {
-        std::optional<fp::ExceptionBehavior> EB = CI->getExceptionBehavior();
-        if (EB && *EB != fp::ebIgnore)
-          return nullptr;
-        U = APFloat::getQNaN(U.getSemantics());
+        return ConstantFP::get(Ty, U);
       }
-      return ConstantFP::get(Ty, U);
     }
 
     // NVVM float/double to signed/unsigned int32/int64 conversions:
@@ -3350,7 +3352,7 @@ static Constant *ConstantFoldNextToward(const APFloat &Op0, const APFloat &Op1,
 
 static Constant *ConstantFoldLibCall2(StringRef Name, Type *Ty,
                                       ArrayRef<Constant *> Operands,
-                                      const TargetLibraryInfo *TLI) {
+                                      const TargetLibraryInfo *TLI = nullptr) {
   if (!TLI)
     return nullptr;
 
@@ -3421,7 +3423,7 @@ static Constant *ConstantFoldLibCall2(StringRef Name, Type *Ty,
 
 static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
                                             ArrayRef<Constant *> Operands,
-                                            const CallBase *Call) {
+                                            const CallBase *Call = nullptr) {
   assert(Operands.size() == 2 && "Wrong number of operands.");
 
   if (Ty->isFloatingPointTy()) {
@@ -4098,11 +4100,10 @@ static Constant *ConstantFoldAMDGCNPermIntrinsic(ArrayRef<Constant *> Operands,
 }
 
 static Constant *ConstantFoldScalarCall3(StringRef Name,
-                                         Intrinsic::ID IntrinsicID,
-                                         Type *Ty,
+                                         Intrinsic::ID IntrinsicID, Type *Ty,
                                          ArrayRef<Constant *> Operands,
-                                         const TargetLibraryInfo *TLI,
-                                         const CallBase *Call) {
+                                         const TargetLibraryInfo *TLI = nullptr,
+                                         const CallBase *Call = nullptr) {
   assert(Operands.size() == 3 && "Wrong number of operands.");
 
   if (const auto *Op1 = dyn_cast<ConstantFP>(Operands[0])) {
@@ -4112,7 +4113,8 @@ static Constant *ConstantFoldScalarCall3(StringRef Name,
         const APFloat &C2 = Op2->getValueAPF();
         const APFloat &C3 = Op3->getValueAPF();
 
-        if (const auto *ConstrIntr = dyn_cast<ConstrainedFPIntrinsic>(Call)) {
+        if (const auto *ConstrIntr =
+                dyn_cast_or_null<ConstrainedFPIntrinsic>(Call)) {
           RoundingMode RM = getEvaluationRoundingMode(ConstrIntr);
           APFloat Res = C1;
           APFloat::opStatus St;
@@ -4262,11 +4264,10 @@ static Constant *ConstantFoldScalarCall3(StringRef Name,
 }
 
 static Constant *ConstantFoldScalarCall(StringRef Name,
-                                        Intrinsic::ID IntrinsicID,
-                                        Type *Ty,
+                                        Intrinsic::ID IntrinsicID, Type *Ty,
                                         ArrayRef<Constant *> Operands,
-                                        const TargetLibraryInfo *TLI,
-                                        const CallBase *Call) {
+                                        const TargetLibraryInfo *TLI = nullptr,
+                                        const CallBase *Call = nullptr) {
   if (IntrinsicID != Intrinsic::not_intrinsic &&
       any_of(Operands, IsaPred<PoisonValue>) &&
       intrinsicPropagatesPoison(IntrinsicID))
@@ -4708,12 +4709,17 @@ ConstantFoldStructCall(StringRef Name, Intrinsic::ID IntrinsicID,
 
 Constant *llvm::ConstantFoldUnaryIntrinsic(Intrinsic::ID ID, Constant *Op,
                                            Type *Ty) {
-  return ConstantFoldScalarCall1("", ID, Ty, Op, nullptr, nullptr);
+  return ConstantFoldScalarCall1("", ID, Ty, Op);
 }
 
 Constant *llvm::ConstantFoldBinaryIntrinsic(Intrinsic::ID ID, Constant *LHS,
                                             Constant *RHS, Type *Ty) {
-  return ConstantFoldIntrinsicCall2(ID, Ty, {LHS, RHS}, nullptr);
+  return ConstantFoldIntrinsicCall2(ID, Ty, {LHS, RHS});
+}
+
+Constant *llvm::ConstantFoldIntrinsic(Intrinsic::ID ID,
+                                      ArrayRef<Constant *> Ops, Type *Ty) {
+  return ConstantFoldScalarCall("", ID, Ty, Ops);
 }
 
 Constant *llvm::ConstantFoldCall(const CallBase *Call, Function *F,
diff --git a/llvm/lib/IR/IRBuilder.cpp b/llvm/lib/IR/IRBuilder.cpp
index eac591af3f495..46a52d94ec7f4 100644
--- a/llvm/lib/IR/IRBuilder.cpp
+++ b/llvm/lib/IR/IRBuilder.cpp
@@ -966,7 +966,9 @@ Value *IRBuilderBase::CreateIntrinsic(Intrinsic::ID ID,
                                       FMFSource FMFSource, const Twine &Name,
                                       ArrayRef<OperandBundleDef> OpBundles,
                                       function_ref<void(CallInst *)> SetFn) {
-  // TODO: Try to constant-fold.
+  Type *RetTy = Intrinsic::getType(Context, ID, OverloadTypes)->getReturnType();
+  if (Value *V = Folder.FoldIntrinsic(ID, Args, RetTy, FMFSource.get(FMF)))
+    return V;
   CallInst *CI = CreateIntrinsicWithoutFolding(ID, OverloadTypes, Args,
                                                FMFSource, Name, OpBundles);
   SetFn(CI);
@@ -977,7 +979,8 @@ Value *IRBuilderBase::CreateIntrinsic(Type *RetTy, Intrinsic::ID ID,
                                       ArrayRef<Value *> Args,
                                       FMFSource FMFSource, const Twine &Name,
                                       function_ref<void(CallInst *)> SetFn) {
-  // TODO: Try to constant-fold.
+  if (Value *V = Folder.FoldIntrinsic(ID, Args, RetTy, FMFSource.get(FMF)))
+    return V;
   CallInst *CI =
       CreateIntrinsicWithoutFolding(RetTy, ID, Args, FMFSource, Name);
   SetFn(CI);
diff --git a/llvm/test/Transforms/IRCE/correct-loop-info.ll b/llvm/test/Transforms/IRCE/correct-loop-info.ll
index c75de167681ad..7ea445718289a 100644
--- a/llvm/test/Transforms/IRCE/correct-loop-info.ll
+++ b/llvm/test/Transforms/IRCE/correct-loop-info.ll
@@ -13,33 +13,30 @@ source_filename = "correct-loop-info.ll"
 define void @baz() personality ptr @ham {
 ; CHECK-LABEL: @baz(
 ; CHECK-NEXT:  bb:
-; CHECK-NEXT:    [[EXIT_PRELOOP_AT:%.*]] = call i32 @llvm.smax.i32(i32 undef, i32 -1)
-; CHECK-NEXT:    [[EXIT_MAINLOOP_AT:%.*]] = call i32 @llvm.smax.i32(i32 undef, i32 0)
 ; CHECK-NEXT:    br label [[OUTERHEADER:%.*]]
 ; CHECK:       outerheader:
 ; CHECK-NEXT:    [[TMP:%.*]] = icmp slt i32 undef, 84
 ; CHECK-NEXT:    br i1 [[TMP]], label [[BB2:%.*]], label [[BB16:%.*]]
 ; CHECK:       bb2:
-; CHECK-NEXT:    [[TMP0:%.*]] = icmp slt i32 undef, [[EXIT_PRELOOP_AT]]
-; CHECK-NEXT:    br i1 [[TMP0]], label [[INNERHEADER_PRELOOP_PREHEADER:%.*]], label [[PRELOOP_PSEUDO_EXIT:%.*]]
+; CHECK-NEXT:    br i1 false, label [[INNERHEADER_PRELOOP_PREHEADER:%.*]], label [[PRELOOP_PSEUDO_EXIT:%.*]]
 ; CHECK:       innerheader.preloop.preheader:
 ; CHECK-NEXT:    br label [[INNERHEADER_PRELOOP:%.*]]
 ; CHECK:       mainloop:
-; CHECK-NEXT:    [[TMP1:%.*]] = icmp slt i32 [[INDVAR_END:%.*]], [[EXIT_MAINLOOP_AT]]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp slt i32 [[INDVAR_END:%.*]], 2147483647
 ; CHECK-NEXT:    br i1 [[TMP1]], label [[INNERHEADER_PREHEADER:%.*]], label [[MAIN_PSEUDO_EXIT:%.*]]
 ; CHECK:       innerheader.preheader:
 ; CHECK-NEXT:    br label [[INNERHEADER:%.*]]
 ; CHECK:       innerheader:
 ; CHECK-NEXT:    [[TMP4:%.*]] = phi i32 [ [[TMP6:%.*]], [[BB8:%.*]] ], [ [[TMP4_PRELOOP_COPY:%.*]], [[INNERHEADER_PREHEADER]] ]
 ; CHECK-NEXT:    invoke void @pluto()
-; CHECK-NEXT:    to label [[BB5:%.*]] unwind label [[OUTER_EXITING_LOOPEXIT_SPLIT_LP_LOOPEXIT_SPLIT_LP:%.*]]
+; CHECK-NEXT:            to label [[BB5:%.*]] unwind label [[OUTER_EXITING_LOOPEXIT_SPLIT_LP_LOOPEXIT_SPLIT_LP:%.*]]
 ; CHECK:       bb5:
 ; CHECK-NEXT:    [[TMP6]] = add nsw i32 [[TMP4]], 1
 ; CH...
[truncated]

@dtcxzyw dtcxzyw left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I checked the logic and it looks fine. No crashes reported by llvm-opt-benchmark. I cannot run fuzzing tests with csmith because I have a running experiment on the machine :)

Please wait for additional approval from other reviewers.

@github-actions

github-actions Bot commented Jun 21, 2026

Copy link
Copy Markdown

🐧 Linux x64 Test Results

  • 197127 tests passed
  • 5400 tests skipped

✅ The build succeeded and all tests passed.

Comment thread llvm/include/llvm/IR/IRBuilderFolder.h Outdated
Comment thread llvm/include/llvm/Analysis/TargetFolder.h Outdated
Comment thread llvm/include/llvm/Analysis/InstSimplifyFolder.h Outdated
@llvmorg-github-actions llvmorg-github-actions Bot added vectorizers llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes labels Jun 21, 2026
@github-actions

github-actions Bot commented Jun 21, 2026

Copy link
Copy Markdown

⚠️ undef deprecator found issues in your code. ⚠️

You can test this locally with the following command:
git diff -U0 --pickaxe-regex -S '([^a-zA-Z0-9#_-]undef([^a-zA-Z0-9_-]|$)|UndefValue::get)' 'HEAD~1' HEAD llvm/include/llvm/Analysis/ConstantFolding.h llvm/include/llvm/Analysis/InstSimplifyFolder.h llvm/include/llvm/Analysis/InstructionSimplify.h llvm/include/llvm/Analysis/TargetFolder.h llvm/include/llvm/IR/ConstantFolder.h llvm/include/llvm/IR/IRBuilderFolder.h llvm/include/llvm/IR/NoFolder.h llvm/lib/Analysis/ConstantFolding.cpp llvm/lib/Analysis/InstructionSimplify.cpp llvm/lib/IR/IRBuilder.cpp llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp llvm/test/CodeGen/VE/Scalar/atomic.ll llvm/test/CodeGen/VE/Scalar/atomic_cmp_swap.ll llvm/test/Transforms/IRCE/correct-loop-info.ll llvm/test/Transforms/IndVarSimplify/lcssa-preservation.ll llvm/test/Transforms/LoopUnroll/runtime-loop-multiexit-dom-verify.ll llvm/test/Transforms/LoopVectorize/X86/replicating-load-store-costs.ll llvm/test/Transforms/LoopVectorize/trip-count-expansion-may-introduce-ub.ll llvm/test/Transforms/LoopVectorize/version-mem-access.ll llvm/test/Transforms/VectorCombine/AArch64/shuffle-of-intrinsics.ll llvm/test/Transforms/VectorCombine/AArch64/shuffletoidentity.ll llvm/test/Transforms/VectorCombine/RISCV/shuffle-of-intrinsics.ll llvm/test/Transforms/VectorCombine/X86/shuffle-of-intrinsics.ll polly/test/CodeGen/scop_expander_insert_point.ll

The following files introduce new uses of undef:

  • polly/test/CodeGen/scop_expander_insert_point.ll

Undef is now deprecated and should only be used in the rare cases where no replacement is possible. For example, a load of uninitialized memory yields undef. You should use poison values for placeholders instead.

In tests, avoid using undef and having tests that trigger undefined behavior. If you need an operand with some unimportant value, you can add a new argument to the function and use that instead.

For example, this is considered a bad practice:

define void @fn() {
  ...
  br i1 undef, ...
}

Please use the following instead:

define void @fn(i1 %cond) {
  ...
  br i1 %cond, ...
}

Please refer to the Undefined Behavior Manual for more information.

#define LLVM_ANALYSIS_TARGETFOLDER_H

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVectorExtras.h"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed anymore?

;
entry:
%2 = call <4 x i1> @llvm.is.fpclass.v4f32(<4 x float> %0, i32 0)
%3 = call <4 x i1> @llvm.is.fpclass.v4f32(<4 x float> %1, i32 0)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably change the argument here so it doesn't fold awy? I don't think it tests what it's intended to otherwise.

; CHECK-NEXT: [[TMP2:%.*]] = shufflevector <4 x float> [[TMP0:%.*]], <4 x float> [[TMP1:%.*]], <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>
; CHECK-NEXT: [[TMP3:%.*]] = call <8 x i1> @llvm.is.fpclass.v8f32(<8 x float> [[TMP2]], i32 0)
; CHECK-NEXT: ret <8 x i1> [[TMP3]]
; CHECK-NEXT: ret <8 x i1> zeroinitializer

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar here (and the other is.fpclass 0 uses).

@artagnon artagnon force-pushed the irbuilder-fold-intr branch from 9192357 to 173e781 Compare June 21, 2026 17:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend:RISC-V llvm:analysis Includes value tracking, cost tables and constant folding llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:ir llvm:transforms llvm:vectorcombine Cost-based vector combine pass vectorizers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants