[llvm-branch-commits] [llvm] InstCombine: Handle log/log2/log10 in SimplifyDemandedFPClass (PR #173881)
Matt Arsenault via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Mon Jan 5 02:04:54 PST 2026
https://github.com/arsenm updated https://github.com/llvm/llvm-project/pull/173881
>From cb094e28bbb7a8ab161cf283273b99984b943a49 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Mon, 22 Dec 2025 23:06:20 +0100
Subject: [PATCH] InstCombine: Handle log/log2/log10 in SimplifyDemandedFPClass
---
llvm/include/llvm/ADT/FloatingPointMode.h | 5 ++
llvm/include/llvm/Support/KnownFPClass.h | 4 ++
llvm/lib/Analysis/ValueTracking.cpp | 46 +++++++------------
llvm/lib/Support/KnownFPClass.cpp | 17 +++++++
.../InstCombineSimplifyDemanded.cpp | 37 +++++++++++++++
.../simplify-demanded-fpclass-log.ll | 27 ++++-------
6 files changed, 88 insertions(+), 48 deletions(-)
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 f151a21914d26..c97f40b5252f7 100644
--- a/llvm/include/llvm/Support/KnownFPClass.h
+++ b/llvm/include/llvm/Support/KnownFPClass.h
@@ -265,6 +265,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 32138b71f80e1..5ee408304f9bb 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5364,44 +5364,30 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
case Intrinsic::experimental_constrained_log2:
case Intrinsic::amdgcn_log: {
Type *EltTy = II->getType()->getScalarType();
- if (IID == Intrinsic::amdgcn_log && EltTy->isFloatTy())
- Known.knownNot(fcSubnormal);
-
- Known.knownNot(fcNegZero);
// log(+inf) -> +inf
// log([+-]0.0) -> -inf
// log(-inf) -> nan
// log(-x) -> nan
- if ((InterestedClasses & (fcNan | fcInf)) == fcNone)
- break;
-
- FPClassTest InterestedSrcs = InterestedClasses;
- if ((InterestedClasses & fcNegInf) != fcNone)
- InterestedSrcs |= fcZero | fcSubnormal;
- if ((InterestedClasses & fcNan) != fcNone)
- InterestedSrcs |= fcNan | (fcNegative & ~fcNan);
-
- KnownFPClass KnownSrc;
- 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;
+ if ((InterestedClasses & (fcNan | fcInf)) != fcNone) {
+ FPClassTest InterestedSrcs = InterestedClasses;
+ if ((InterestedClasses & fcNegInf) != fcNone)
+ InterestedSrcs |= fcZero | fcSubnormal;
+ if ((InterestedClasses & fcNan) != fcNone)
+ InterestedSrcs |= fcNan | fcNegative;
- const fltSemantics &FltSem = EltTy->getFltSemantics();
- DenormalMode Mode = F->getDenormalMode(FltSem);
+ KnownFPClass KnownSrc;
+ computeKnownFPClass(II->getArgOperand(0), DemandedElts, InterestedSrcs,
+ KnownSrc, Q, Depth + 1);
- if (KnownSrc.isKnownNeverLogicalZero(Mode))
- Known.knownNot(fcNegInf);
+ const Function *F = II->getFunction();
+ DenormalMode Mode = F ? F->getDenormalMode(EltTy->getFltSemantics())
+ : DenormalMode::getDynamic();
+ Known = KnownFPClass::log(KnownSrc, Mode);
+ }
+ if (IID == Intrinsic::amdgcn_log && EltTy->isFloatTy())
+ Known.knownNot(fcSubnormal);
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 b76d78a4376e3..9f0d0fc36a825 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
@@ -2348,6 +2348,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 2c6d11378e167..455d37019c965 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
@@ -99,8 +98,7 @@ define nofpclass(ninf) float @ret_nofpclass_ninf_log(i1 %cond, float %x, float n
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
@@ -112,8 +110,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
@@ -125,8 +122,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
@@ -138,8 +134,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
@@ -164,8 +159,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
@@ -203,8 +197,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
@@ -216,8 +209,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
@@ -242,8 +234,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
More information about the llvm-branch-commits
mailing list