Skip to content

Conversation

@arsenm
Copy link
Contributor

@arsenm arsenm commented Dec 29, 2025

No description provided.

Copy link
Contributor Author

arsenm commented Dec 29, 2025

@arsenm arsenm added floating-point Floating-point math llvm:analysis Includes value tracking, cost tables and constant folding llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:support llvm:transforms labels Dec 29, 2025 — with Graphite App
@arsenm arsenm marked this pull request as ready for review December 29, 2025 17:00
@llvmbot
Copy link
Member

llvmbot commented Dec 29, 2025

@llvm/pr-subscribers-llvm-adt
@llvm/pr-subscribers-llvm-analysis

@llvm/pr-subscribers-llvm-support

Author: Matt Arsenault (arsenm)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/173881.diff

6 Files Affected:

  • (modified) llvm/include/llvm/ADT/FloatingPointMode.h (+5)
  • (modified) llvm/include/llvm/Support/KnownFPClass.h (+4)
  • (modified) llvm/lib/Analysis/ValueTracking.cpp (+3-15)
  • (modified) llvm/lib/Support/KnownFPClass.cpp (+17)
  • (modified) llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp (+37)
  • (modified) llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-log.ll (+12-23)
diff --git a/llvm/include/llvm/ADT/FloatingPointMode.h b/llvm/include/llvm/ADT/FloatingPointMode.h
index a9702c65e631f..0605e0b4f4cf9 100644
--- a/llvm/include/llvm/ADT/FloatingPointMode.h
+++ b/llvm/include/llvm/ADT/FloatingPointMode.h
@@ -153,6 +153,11 @@ struct DenormalMode {
            Input == DenormalModeKind::PositiveZero;
   }
 
+  /// Return true if input denormals may be implicitly treated as 0.
+  constexpr bool inputsMayBeZero() const {
+    return inputsAreZero() || Input == DenormalMode::Dynamic;
+  }
+
   /// Return true if output denormals should be flushed to 0.
   constexpr bool outputsAreZero() const {
     return Output == DenormalModeKind::PreserveSign ||
diff --git a/llvm/include/llvm/Support/KnownFPClass.h b/llvm/include/llvm/Support/KnownFPClass.h
index 62df87ad8a67e..07d74f2867089 100644
--- a/llvm/include/llvm/Support/KnownFPClass.h
+++ b/llvm/include/llvm/Support/KnownFPClass.h
@@ -263,6 +263,10 @@ struct KnownFPClass {
   LLVM_ABI void propagateCanonicalizingSrc(const KnownFPClass &Src,
                                            DenormalMode Mode);
 
+  /// Propagate known class for log/log2/log10
+  static LLVM_ABI KnownFPClass
+  log(const KnownFPClass &Src, DenormalMode Mode = DenormalMode::getDynamic());
+
   void resetAll() { *this = KnownFPClass(); }
 };
 
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index cddd6f9c25074..bac863cb3c67c 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5391,22 +5391,10 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
       computeKnownFPClass(II->getArgOperand(0), DemandedElts, InterestedSrcs,
                           KnownSrc, Q, Depth + 1);
 
-      if (KnownSrc.isKnownNeverPosInfinity())
-        Known.knownNot(fcPosInf);
-
-      if (KnownSrc.isKnownNeverNaN() && KnownSrc.cannotBeOrderedLessThanZero())
-        Known.knownNot(fcNan);
-
       const Function *F = II->getFunction();
-      if (!F)
-        break;
-
-      const fltSemantics &FltSem = EltTy->getFltSemantics();
-      DenormalMode Mode = F->getDenormalMode(FltSem);
-
-      if (KnownSrc.isKnownNeverLogicalZero(Mode))
-        Known.knownNot(fcNegInf);
-
+      DenormalMode Mode = F ? F->getDenormalMode(EltTy->getFltSemantics())
+                            : DenormalMode::getDynamic();
+      Known = KnownFPClass::log(KnownSrc, Mode);
       break;
     }
     case Intrinsic::powi: {
diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index 125bee00c38ff..ff98908fdb2c4 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -226,3 +226,20 @@ void KnownFPClass::propagateCanonicalizingSrc(const KnownFPClass &Src,
   propagateDenormal(Src, Mode);
   propagateNaN(Src, /*PreserveSign=*/true);
 }
+
+KnownFPClass KnownFPClass::log(const KnownFPClass &KnownSrc,
+                               DenormalMode Mode) {
+  KnownFPClass Known;
+  Known.knownNot(fcNegZero);
+
+  if (KnownSrc.isKnownNeverPosInfinity())
+    Known.knownNot(fcPosInf);
+
+  if (KnownSrc.isKnownNeverNaN() && KnownSrc.cannotBeOrderedLessThanZero())
+    Known.knownNot(fcNan);
+
+  if (KnownSrc.isKnownNeverLogicalZero(Mode))
+    Known.knownNot(fcNegInf);
+
+  return Known;
+}
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
index 33ece6c2b69d8..3cb4c629b9108 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
@@ -2202,6 +2202,43 @@ Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Value *V,
       Known = KnownFPClass::exp(KnownSrc);
       break;
     }
+    case Intrinsic::log:
+    case Intrinsic::log2:
+    case Intrinsic::log10: {
+      FPClassTest DemandedSrcMask = DemandedMask & (fcNan | fcPosInf);
+
+      Type *EltTy = VTy->getScalarType();
+      DenormalMode Mode = F.getDenormalMode(EltTy->getFltSemantics());
+
+      // log(x < 0) = nan
+      if (DemandedMask & fcNan)
+        DemandedSrcMask |= (fcNegative & ~fcNegZero);
+
+      // log(0) = -inf
+      if (DemandedMask & fcNegInf) {
+        DemandedSrcMask |= fcZero;
+
+        // No value produces subnormal result.
+        if (Mode.inputsMayBeZero())
+          DemandedSrcMask |= fcSubnormal;
+      }
+
+      if (DemandedMask & fcNormal)
+        DemandedSrcMask |= fcNormal | fcSubnormal;
+
+      // log(1) = 0
+      if (DemandedMask & fcZero)
+        DemandedSrcMask |= fcPosNormal;
+
+      KnownFPClass KnownSrc;
+      if (SimplifyDemandedFPClass(I, 0, DemandedSrcMask, KnownSrc, Depth + 1))
+        return I;
+
+      Known = KnownFPClass::log(KnownSrc, Mode);
+
+      FPClassTest ValidResults = DemandedMask & Known.KnownFPClasses;
+      return getFPClassConstant(VTy, ValidResults, /*IsCanonicalizing=*/true);
+    }
     case Intrinsic::canonicalize: {
       Type *EltTy = VTy->getScalarType();
 
diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-log.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-log.ll
index d6f2cab6687ab..87973fa36b478 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-log.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-log.ll
@@ -5,8 +5,7 @@
 define nofpclass(inf norm sub zero) float @ret_nofpclass_only_nan__log(float %unknown) {
 ; CHECK-LABEL: define nofpclass(inf zero sub norm) float @ret_nofpclass_only_nan__log(
 ; CHECK-SAME: float [[UNKNOWN:%.*]]) {
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[UNKNOWN]])
-; CHECK-NEXT:    ret float [[RESULT]]
+; CHECK-NEXT:    ret float 0x7FF8000000000000
 ;
   %result = call float @llvm.log.f32(float %unknown)
   ret float %result
@@ -16,8 +15,7 @@ define nofpclass(inf norm sub zero) float @ret_nofpclass_only_nan__log(float %un
 define nofpclass(nan inf norm sub) float @ret_nofpclass_only_zero__log(float %unknown) {
 ; CHECK-LABEL: define nofpclass(nan inf sub norm) float @ret_nofpclass_only_zero__log(
 ; CHECK-SAME: float [[UNKNOWN:%.*]]) {
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[UNKNOWN]])
-; CHECK-NEXT:    ret float [[RESULT]]
+; CHECK-NEXT:    ret float 0.000000e+00
 ;
   %result = call float @llvm.log.f32(float %unknown)
   ret float %result
@@ -57,7 +55,7 @@ define nofpclass(nan pinf norm sub zero) float @ret_nofpclass_only_ninf__log(flo
 define nofpclass(nan inf norm sub pzero) float @ret_nofpclass_only_nzero__log(float %unknown) {
 ; CHECK-LABEL: define nofpclass(nan inf pzero sub norm) float @ret_nofpclass_only_nzero__log(
 ; CHECK-SAME: float [[UNKNOWN:%.*]]) {
-; CHECK-NEXT:    ret float -0.000000e+00
+; CHECK-NEXT:    ret float poison
 ;
   %result = call float @llvm.log.f32(float %unknown)
   ret float %result
@@ -87,8 +85,7 @@ define nofpclass(norm sub zero) float @ret_nofpclass_only_inf_nan__log(float %un
 define nofpclass(ninf) float @ret_nofpclass_ninf_log(i1 %cond, float %x) {
 ; CHECK-LABEL: define nofpclass(ninf) float @ret_nofpclass_ninf_log(
 ; CHECK-SAME: i1 [[COND:%.*]], float [[X:%.*]]) {
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float 0.000000e+00, float [[X]]
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[SELECT]])
+; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[X]])
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %select = select i1 %cond, float 0.0, float %x
@@ -100,8 +97,7 @@ define nofpclass(ninf) float @ret_nofpclass_ninf_log(i1 %cond, float %x) {
 define nofpclass(pinf) float @ret_nofpclass_pinf_log_select_inf_or_unknown(i1 %cond, float %unknown) {
 ; CHECK-LABEL: define nofpclass(pinf) float @ret_nofpclass_pinf_log_select_inf_or_unknown(
 ; CHECK-SAME: i1 [[COND:%.*]], float [[UNKNOWN:%.*]]) {
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float 0x7FF0000000000000, float [[UNKNOWN]]
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[SELECT]])
+; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[UNKNOWN]])
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %select = select i1 %cond, float 0x7ff0000000000000, float %unknown
@@ -113,8 +109,7 @@ define nofpclass(pinf) float @ret_nofpclass_pinf_log_select_inf_or_unknown(i1 %c
 define nofpclass(pinf) float @ret_nofpclass_pinf_log2_select_inf_or_unknown(i1 %cond, float %unknown) {
 ; CHECK-LABEL: define nofpclass(pinf) float @ret_nofpclass_pinf_log2_select_inf_or_unknown(
 ; CHECK-SAME: i1 [[COND:%.*]], float [[UNKNOWN:%.*]]) {
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float 0x7FF0000000000000, float [[UNKNOWN]]
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log2.f32(float [[SELECT]])
+; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log2.f32(float [[UNKNOWN]])
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %select = select i1 %cond, float 0x7ff0000000000000, float %unknown
@@ -126,8 +121,7 @@ define nofpclass(pinf) float @ret_nofpclass_pinf_log2_select_inf_or_unknown(i1 %
 define nofpclass(pinf) float @ret_nofpclass_pinf_log10_select_inf_or_unknown(i1 %cond, float %unknown) {
 ; CHECK-LABEL: define nofpclass(pinf) float @ret_nofpclass_pinf_log10_select_inf_or_unknown(
 ; CHECK-SAME: i1 [[COND:%.*]], float [[UNKNOWN:%.*]]) {
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float 0x7FF0000000000000, float [[UNKNOWN]]
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log10.f32(float [[SELECT]])
+; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log10.f32(float [[UNKNOWN]])
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %select = select i1 %cond, float 0x7ff0000000000000, float %unknown
@@ -139,8 +133,7 @@ define nofpclass(pinf) float @ret_nofpclass_pinf_log10_select_inf_or_unknown(i1
 define nofpclass(ninf norm zero) float @ret_nofpclass_nan_or_sub__log_select__finite_positive__unknown(i1 %cond, float nofpclass(inf nnorm nsub nan) %must.be.finite.positive, float %unknown) {
 ; CHECK-LABEL: define nofpclass(ninf zero norm) float @ret_nofpclass_nan_or_sub__log_select__finite_positive__unknown(
 ; CHECK-SAME: i1 [[COND:%.*]], float nofpclass(nan inf nsub nnorm) [[MUST_BE_FINITE_POSITIVE:%.*]], float [[UNKNOWN:%.*]]) {
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float [[MUST_BE_FINITE_POSITIVE]], float [[UNKNOWN]]
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[SELECT]])
+; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[UNKNOWN]])
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %select = select i1 %cond, float %must.be.finite.positive, float %unknown
@@ -165,8 +158,7 @@ define nofpclass(ninf norm zero) float @ret_nofpclass_nan_or_sub__log_select__fi
 define nofpclass(pinf nan norm zero) float @ret_ninf_or_sub__log_select__pinf_or_sub_orzero__else_not0__ieee(i1 %cond, float nofpclass(ninf norm nan) %must.be.pinf.or.sub.or.zero, float nofpclass(zero) %not.zero) {
 ; CHECK-LABEL: define nofpclass(nan pinf zero norm) float @ret_ninf_or_sub__log_select__pinf_or_sub_orzero__else_not0__ieee(
 ; CHECK-SAME: i1 [[COND:%.*]], float nofpclass(nan ninf norm) [[MUST_BE_PINF_OR_SUB_OR_ZERO:%.*]], float nofpclass(zero) [[NOT_ZERO:%.*]]) {
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float [[MUST_BE_PINF_OR_SUB_OR_ZERO]], float [[NOT_ZERO]]
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[SELECT]])
+; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[MUST_BE_PINF_OR_SUB_OR_ZERO]])
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %select = select i1 %cond, float %must.be.pinf.or.sub.or.zero, float %not.zero
@@ -204,8 +196,7 @@ define nofpclass(pinf nan norm zero) float @ret_ninf_or_sub__log_select__pinf_or
 define nofpclass(inf nan pnorm sub zero) float @ret_only_nnorm__log__select_unknown_or_infnan(i1 %cond, float %unknown, float nofpclass(norm sub zero) %b) {
 ; CHECK-LABEL: define nofpclass(nan inf zero sub pnorm) float @ret_only_nnorm__log__select_unknown_or_infnan(
 ; CHECK-SAME: i1 [[COND:%.*]], float [[UNKNOWN:%.*]], float nofpclass(zero sub norm) [[B:%.*]]) {
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float [[UNKNOWN]], float [[B]]
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[SELECT]])
+; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[UNKNOWN]])
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %select = select i1 %cond, float %unknown, float %b
@@ -217,8 +208,7 @@ define nofpclass(inf nan pnorm sub zero) float @ret_only_nnorm__log__select_unkn
 define nofpclass(inf nan nnorm sub zero) float @ret_only_pnorm__log__select_unknown_or_infnan(i1 %cond, float %unknown, float nofpclass(norm sub zero) %b) {
 ; CHECK-LABEL: define nofpclass(nan inf zero sub nnorm) float @ret_only_pnorm__log__select_unknown_or_infnan(
 ; CHECK-SAME: i1 [[COND:%.*]], float [[UNKNOWN:%.*]], float nofpclass(zero sub norm) [[B:%.*]]) {
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float [[UNKNOWN]], float [[B]]
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[SELECT]])
+; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[UNKNOWN]])
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %select = select i1 %cond, float %unknown, float %b
@@ -243,8 +233,7 @@ define nofpclass(nan inf norm) float @ret_only_zero_sub__log__select_pnormnan_or
 define nofpclass(nan inf norm) float @ret_only_zero_sub__log__select_nnormnan_or_unknown(i1 %cond, float nofpclass(inf sub zero pnorm) %nnorm.or.nan, float %b) {
 ; CHECK-LABEL: define nofpclass(nan inf norm) float @ret_only_zero_sub__log__select_nnormnan_or_unknown(
 ; CHECK-SAME: i1 [[COND:%.*]], float nofpclass(inf zero sub pnorm) [[NNORM_OR_NAN:%.*]], float [[B:%.*]]) {
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float [[NNORM_OR_NAN]], float [[B]]
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[SELECT]])
+; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[B]])
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %select = select i1 %cond, float %nnorm.or.nan, float %b

@llvmbot
Copy link
Member

llvmbot commented Dec 29, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Matt Arsenault (arsenm)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/173881.diff

6 Files Affected:

  • (modified) llvm/include/llvm/ADT/FloatingPointMode.h (+5)
  • (modified) llvm/include/llvm/Support/KnownFPClass.h (+4)
  • (modified) llvm/lib/Analysis/ValueTracking.cpp (+3-15)
  • (modified) llvm/lib/Support/KnownFPClass.cpp (+17)
  • (modified) llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp (+37)
  • (modified) llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-log.ll (+12-23)
diff --git a/llvm/include/llvm/ADT/FloatingPointMode.h b/llvm/include/llvm/ADT/FloatingPointMode.h
index a9702c65e631f..0605e0b4f4cf9 100644
--- a/llvm/include/llvm/ADT/FloatingPointMode.h
+++ b/llvm/include/llvm/ADT/FloatingPointMode.h
@@ -153,6 +153,11 @@ struct DenormalMode {
            Input == DenormalModeKind::PositiveZero;
   }
 
+  /// Return true if input denormals may be implicitly treated as 0.
+  constexpr bool inputsMayBeZero() const {
+    return inputsAreZero() || Input == DenormalMode::Dynamic;
+  }
+
   /// Return true if output denormals should be flushed to 0.
   constexpr bool outputsAreZero() const {
     return Output == DenormalModeKind::PreserveSign ||
diff --git a/llvm/include/llvm/Support/KnownFPClass.h b/llvm/include/llvm/Support/KnownFPClass.h
index 62df87ad8a67e..07d74f2867089 100644
--- a/llvm/include/llvm/Support/KnownFPClass.h
+++ b/llvm/include/llvm/Support/KnownFPClass.h
@@ -263,6 +263,10 @@ struct KnownFPClass {
   LLVM_ABI void propagateCanonicalizingSrc(const KnownFPClass &Src,
                                            DenormalMode Mode);
 
+  /// Propagate known class for log/log2/log10
+  static LLVM_ABI KnownFPClass
+  log(const KnownFPClass &Src, DenormalMode Mode = DenormalMode::getDynamic());
+
   void resetAll() { *this = KnownFPClass(); }
 };
 
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index cddd6f9c25074..bac863cb3c67c 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5391,22 +5391,10 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
       computeKnownFPClass(II->getArgOperand(0), DemandedElts, InterestedSrcs,
                           KnownSrc, Q, Depth + 1);
 
-      if (KnownSrc.isKnownNeverPosInfinity())
-        Known.knownNot(fcPosInf);
-
-      if (KnownSrc.isKnownNeverNaN() && KnownSrc.cannotBeOrderedLessThanZero())
-        Known.knownNot(fcNan);
-
       const Function *F = II->getFunction();
-      if (!F)
-        break;
-
-      const fltSemantics &FltSem = EltTy->getFltSemantics();
-      DenormalMode Mode = F->getDenormalMode(FltSem);
-
-      if (KnownSrc.isKnownNeverLogicalZero(Mode))
-        Known.knownNot(fcNegInf);
-
+      DenormalMode Mode = F ? F->getDenormalMode(EltTy->getFltSemantics())
+                            : DenormalMode::getDynamic();
+      Known = KnownFPClass::log(KnownSrc, Mode);
       break;
     }
     case Intrinsic::powi: {
diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index 125bee00c38ff..ff98908fdb2c4 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -226,3 +226,20 @@ void KnownFPClass::propagateCanonicalizingSrc(const KnownFPClass &Src,
   propagateDenormal(Src, Mode);
   propagateNaN(Src, /*PreserveSign=*/true);
 }
+
+KnownFPClass KnownFPClass::log(const KnownFPClass &KnownSrc,
+                               DenormalMode Mode) {
+  KnownFPClass Known;
+  Known.knownNot(fcNegZero);
+
+  if (KnownSrc.isKnownNeverPosInfinity())
+    Known.knownNot(fcPosInf);
+
+  if (KnownSrc.isKnownNeverNaN() && KnownSrc.cannotBeOrderedLessThanZero())
+    Known.knownNot(fcNan);
+
+  if (KnownSrc.isKnownNeverLogicalZero(Mode))
+    Known.knownNot(fcNegInf);
+
+  return Known;
+}
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
index 33ece6c2b69d8..3cb4c629b9108 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
@@ -2202,6 +2202,43 @@ Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Value *V,
       Known = KnownFPClass::exp(KnownSrc);
       break;
     }
+    case Intrinsic::log:
+    case Intrinsic::log2:
+    case Intrinsic::log10: {
+      FPClassTest DemandedSrcMask = DemandedMask & (fcNan | fcPosInf);
+
+      Type *EltTy = VTy->getScalarType();
+      DenormalMode Mode = F.getDenormalMode(EltTy->getFltSemantics());
+
+      // log(x < 0) = nan
+      if (DemandedMask & fcNan)
+        DemandedSrcMask |= (fcNegative & ~fcNegZero);
+
+      // log(0) = -inf
+      if (DemandedMask & fcNegInf) {
+        DemandedSrcMask |= fcZero;
+
+        // No value produces subnormal result.
+        if (Mode.inputsMayBeZero())
+          DemandedSrcMask |= fcSubnormal;
+      }
+
+      if (DemandedMask & fcNormal)
+        DemandedSrcMask |= fcNormal | fcSubnormal;
+
+      // log(1) = 0
+      if (DemandedMask & fcZero)
+        DemandedSrcMask |= fcPosNormal;
+
+      KnownFPClass KnownSrc;
+      if (SimplifyDemandedFPClass(I, 0, DemandedSrcMask, KnownSrc, Depth + 1))
+        return I;
+
+      Known = KnownFPClass::log(KnownSrc, Mode);
+
+      FPClassTest ValidResults = DemandedMask & Known.KnownFPClasses;
+      return getFPClassConstant(VTy, ValidResults, /*IsCanonicalizing=*/true);
+    }
     case Intrinsic::canonicalize: {
       Type *EltTy = VTy->getScalarType();
 
diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-log.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-log.ll
index d6f2cab6687ab..87973fa36b478 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-log.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-log.ll
@@ -5,8 +5,7 @@
 define nofpclass(inf norm sub zero) float @ret_nofpclass_only_nan__log(float %unknown) {
 ; CHECK-LABEL: define nofpclass(inf zero sub norm) float @ret_nofpclass_only_nan__log(
 ; CHECK-SAME: float [[UNKNOWN:%.*]]) {
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[UNKNOWN]])
-; CHECK-NEXT:    ret float [[RESULT]]
+; CHECK-NEXT:    ret float 0x7FF8000000000000
 ;
   %result = call float @llvm.log.f32(float %unknown)
   ret float %result
@@ -16,8 +15,7 @@ define nofpclass(inf norm sub zero) float @ret_nofpclass_only_nan__log(float %un
 define nofpclass(nan inf norm sub) float @ret_nofpclass_only_zero__log(float %unknown) {
 ; CHECK-LABEL: define nofpclass(nan inf sub norm) float @ret_nofpclass_only_zero__log(
 ; CHECK-SAME: float [[UNKNOWN:%.*]]) {
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[UNKNOWN]])
-; CHECK-NEXT:    ret float [[RESULT]]
+; CHECK-NEXT:    ret float 0.000000e+00
 ;
   %result = call float @llvm.log.f32(float %unknown)
   ret float %result
@@ -57,7 +55,7 @@ define nofpclass(nan pinf norm sub zero) float @ret_nofpclass_only_ninf__log(flo
 define nofpclass(nan inf norm sub pzero) float @ret_nofpclass_only_nzero__log(float %unknown) {
 ; CHECK-LABEL: define nofpclass(nan inf pzero sub norm) float @ret_nofpclass_only_nzero__log(
 ; CHECK-SAME: float [[UNKNOWN:%.*]]) {
-; CHECK-NEXT:    ret float -0.000000e+00
+; CHECK-NEXT:    ret float poison
 ;
   %result = call float @llvm.log.f32(float %unknown)
   ret float %result
@@ -87,8 +85,7 @@ define nofpclass(norm sub zero) float @ret_nofpclass_only_inf_nan__log(float %un
 define nofpclass(ninf) float @ret_nofpclass_ninf_log(i1 %cond, float %x) {
 ; CHECK-LABEL: define nofpclass(ninf) float @ret_nofpclass_ninf_log(
 ; CHECK-SAME: i1 [[COND:%.*]], float [[X:%.*]]) {
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float 0.000000e+00, float [[X]]
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[SELECT]])
+; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[X]])
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %select = select i1 %cond, float 0.0, float %x
@@ -100,8 +97,7 @@ define nofpclass(ninf) float @ret_nofpclass_ninf_log(i1 %cond, float %x) {
 define nofpclass(pinf) float @ret_nofpclass_pinf_log_select_inf_or_unknown(i1 %cond, float %unknown) {
 ; CHECK-LABEL: define nofpclass(pinf) float @ret_nofpclass_pinf_log_select_inf_or_unknown(
 ; CHECK-SAME: i1 [[COND:%.*]], float [[UNKNOWN:%.*]]) {
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float 0x7FF0000000000000, float [[UNKNOWN]]
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[SELECT]])
+; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[UNKNOWN]])
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %select = select i1 %cond, float 0x7ff0000000000000, float %unknown
@@ -113,8 +109,7 @@ define nofpclass(pinf) float @ret_nofpclass_pinf_log_select_inf_or_unknown(i1 %c
 define nofpclass(pinf) float @ret_nofpclass_pinf_log2_select_inf_or_unknown(i1 %cond, float %unknown) {
 ; CHECK-LABEL: define nofpclass(pinf) float @ret_nofpclass_pinf_log2_select_inf_or_unknown(
 ; CHECK-SAME: i1 [[COND:%.*]], float [[UNKNOWN:%.*]]) {
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float 0x7FF0000000000000, float [[UNKNOWN]]
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log2.f32(float [[SELECT]])
+; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log2.f32(float [[UNKNOWN]])
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %select = select i1 %cond, float 0x7ff0000000000000, float %unknown
@@ -126,8 +121,7 @@ define nofpclass(pinf) float @ret_nofpclass_pinf_log2_select_inf_or_unknown(i1 %
 define nofpclass(pinf) float @ret_nofpclass_pinf_log10_select_inf_or_unknown(i1 %cond, float %unknown) {
 ; CHECK-LABEL: define nofpclass(pinf) float @ret_nofpclass_pinf_log10_select_inf_or_unknown(
 ; CHECK-SAME: i1 [[COND:%.*]], float [[UNKNOWN:%.*]]) {
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float 0x7FF0000000000000, float [[UNKNOWN]]
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log10.f32(float [[SELECT]])
+; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log10.f32(float [[UNKNOWN]])
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %select = select i1 %cond, float 0x7ff0000000000000, float %unknown
@@ -139,8 +133,7 @@ define nofpclass(pinf) float @ret_nofpclass_pinf_log10_select_inf_or_unknown(i1
 define nofpclass(ninf norm zero) float @ret_nofpclass_nan_or_sub__log_select__finite_positive__unknown(i1 %cond, float nofpclass(inf nnorm nsub nan) %must.be.finite.positive, float %unknown) {
 ; CHECK-LABEL: define nofpclass(ninf zero norm) float @ret_nofpclass_nan_or_sub__log_select__finite_positive__unknown(
 ; CHECK-SAME: i1 [[COND:%.*]], float nofpclass(nan inf nsub nnorm) [[MUST_BE_FINITE_POSITIVE:%.*]], float [[UNKNOWN:%.*]]) {
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float [[MUST_BE_FINITE_POSITIVE]], float [[UNKNOWN]]
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[SELECT]])
+; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[UNKNOWN]])
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %select = select i1 %cond, float %must.be.finite.positive, float %unknown
@@ -165,8 +158,7 @@ define nofpclass(ninf norm zero) float @ret_nofpclass_nan_or_sub__log_select__fi
 define nofpclass(pinf nan norm zero) float @ret_ninf_or_sub__log_select__pinf_or_sub_orzero__else_not0__ieee(i1 %cond, float nofpclass(ninf norm nan) %must.be.pinf.or.sub.or.zero, float nofpclass(zero) %not.zero) {
 ; CHECK-LABEL: define nofpclass(nan pinf zero norm) float @ret_ninf_or_sub__log_select__pinf_or_sub_orzero__else_not0__ieee(
 ; CHECK-SAME: i1 [[COND:%.*]], float nofpclass(nan ninf norm) [[MUST_BE_PINF_OR_SUB_OR_ZERO:%.*]], float nofpclass(zero) [[NOT_ZERO:%.*]]) {
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float [[MUST_BE_PINF_OR_SUB_OR_ZERO]], float [[NOT_ZERO]]
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[SELECT]])
+; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[MUST_BE_PINF_OR_SUB_OR_ZERO]])
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %select = select i1 %cond, float %must.be.pinf.or.sub.or.zero, float %not.zero
@@ -204,8 +196,7 @@ define nofpclass(pinf nan norm zero) float @ret_ninf_or_sub__log_select__pinf_or
 define nofpclass(inf nan pnorm sub zero) float @ret_only_nnorm__log__select_unknown_or_infnan(i1 %cond, float %unknown, float nofpclass(norm sub zero) %b) {
 ; CHECK-LABEL: define nofpclass(nan inf zero sub pnorm) float @ret_only_nnorm__log__select_unknown_or_infnan(
 ; CHECK-SAME: i1 [[COND:%.*]], float [[UNKNOWN:%.*]], float nofpclass(zero sub norm) [[B:%.*]]) {
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float [[UNKNOWN]], float [[B]]
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[SELECT]])
+; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[UNKNOWN]])
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %select = select i1 %cond, float %unknown, float %b
@@ -217,8 +208,7 @@ define nofpclass(inf nan pnorm sub zero) float @ret_only_nnorm__log__select_unkn
 define nofpclass(inf nan nnorm sub zero) float @ret_only_pnorm__log__select_unknown_or_infnan(i1 %cond, float %unknown, float nofpclass(norm sub zero) %b) {
 ; CHECK-LABEL: define nofpclass(nan inf zero sub nnorm) float @ret_only_pnorm__log__select_unknown_or_infnan(
 ; CHECK-SAME: i1 [[COND:%.*]], float [[UNKNOWN:%.*]], float nofpclass(zero sub norm) [[B:%.*]]) {
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float [[UNKNOWN]], float [[B]]
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[SELECT]])
+; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[UNKNOWN]])
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %select = select i1 %cond, float %unknown, float %b
@@ -243,8 +233,7 @@ define nofpclass(nan inf norm) float @ret_only_zero_sub__log__select_pnormnan_or
 define nofpclass(nan inf norm) float @ret_only_zero_sub__log__select_nnormnan_or_unknown(i1 %cond, float nofpclass(inf sub zero pnorm) %nnorm.or.nan, float %b) {
 ; CHECK-LABEL: define nofpclass(nan inf norm) float @ret_only_zero_sub__log__select_nnormnan_or_unknown(
 ; CHECK-SAME: i1 [[COND:%.*]], float nofpclass(inf zero sub pnorm) [[NNORM_OR_NAN:%.*]], float [[B:%.*]]) {
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float [[NNORM_OR_NAN]], float [[B]]
-; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[SELECT]])
+; CHECK-NEXT:    [[RESULT:%.*]] = call float @llvm.log.f32(float [[B]])
 ; CHECK-NEXT:    ret float [[RESULT]]
 ;
   %select = select i1 %cond, float %nnorm.or.nan, float %b

@github-actions
Copy link

github-actions bot commented Dec 29, 2025

🪟 Windows x64 Test Results

  • 129052 tests passed
  • 2841 tests skipped

✅ The build succeeded and all tests passed.

@github-actions
Copy link

github-actions bot commented Dec 29, 2025

🐧 Linux x64 Test Results

  • 188030 tests passed
  • 4988 tests skipped

✅ The build succeeded and all tests passed.

@arsenm arsenm force-pushed the users/arsenm/instcombine/simplify-demanded-fpclass-log branch from 51c1ea4 to bc98197 Compare December 29, 2025 18:21
@arsenm arsenm force-pushed the users/arsenm/instcombine/add-baseline-test-simplifydemandedfpclass-log branch from 61a27d9 to b340145 Compare December 29, 2025 18:21
@arsenm arsenm force-pushed the users/arsenm/instcombine/simplify-demanded-fpclass-log branch from bc98197 to ce1a943 Compare December 29, 2025 22:29
@arsenm arsenm force-pushed the users/arsenm/instcombine/add-baseline-test-simplifydemandedfpclass-log branch from b340145 to 65337de Compare December 29, 2025 22:29
@arsenm arsenm force-pushed the users/arsenm/instcombine/simplify-demanded-fpclass-log branch from ce1a943 to b7cff00 Compare December 30, 2025 18:40
@arsenm arsenm force-pushed the users/arsenm/instcombine/add-baseline-test-simplifydemandedfpclass-log branch 2 times, most recently from 44e3fe0 to 442fbbb Compare December 30, 2025 18:46
@arsenm arsenm force-pushed the users/arsenm/instcombine/simplify-demanded-fpclass-log branch from b7cff00 to 293a863 Compare December 30, 2025 18:46
@arsenm arsenm force-pushed the users/arsenm/instcombine/add-baseline-test-simplifydemandedfpclass-log branch from 442fbbb to c3b27f9 Compare December 30, 2025 21:46
@arsenm arsenm force-pushed the users/arsenm/instcombine/simplify-demanded-fpclass-log branch 2 times, most recently from eb1e33a to d86b4c3 Compare December 31, 2025 09:45
@arsenm arsenm force-pushed the users/arsenm/instcombine/add-baseline-test-simplifydemandedfpclass-log branch from c3b27f9 to 2ed972f Compare December 31, 2025 09:45
Copy link
Member

@dtcxzyw dtcxzyw left a comment

Choose a reason for hiding this comment

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

LGTM

@arsenm arsenm force-pushed the users/arsenm/instcombine/add-baseline-test-simplifydemandedfpclass-log branch from 2ed972f to b60b014 Compare January 5, 2026 10:04
@arsenm arsenm force-pushed the users/arsenm/instcombine/simplify-demanded-fpclass-log branch from d86b4c3 to cb094e2 Compare January 5, 2026 10:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

floating-point Floating-point math llvm:adt llvm:analysis Includes value tracking, cost tables and constant folding llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:support llvm:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants