[llvm-branch-commits] [llvm] InstCombine: Handle multiple use fneg(fabs(x)) in SimplifyDemandedFPClass (PR #176149)
Matt Arsenault via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Jan 15 04:50:14 PST 2026
https://github.com/arsenm created https://github.com/llvm/llvm-project/pull/176149
This ends up being smarter than the single use case, so these should
be merged at some point.
>From 1fae7b3eac2c508a5a0658d5d27dcff64b6e041c Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Thu, 15 Jan 2026 13:04:40 +0100
Subject: [PATCH] InstCombine: Handle multiple use fneg(fabs(x)) in
SimplifyDemandedFPClass
This ends up being smarter than the single use case, so these should
be merged at some point.
---
llvm/include/llvm/Support/KnownFPClass.h | 6 +
.../InstCombineSimplifyDemanded.cpp | 53 ++++--
.../InstCombine/simplify-demanded-fpclass.ll | 169 ++++++++++++++++++
3 files changed, 218 insertions(+), 10 deletions(-)
diff --git a/llvm/include/llvm/Support/KnownFPClass.h b/llvm/include/llvm/Support/KnownFPClass.h
index 6c1a6a7e9b5c3..0b9412460af0c 100644
--- a/llvm/include/llvm/Support/KnownFPClass.h
+++ b/llvm/include/llvm/Support/KnownFPClass.h
@@ -192,6 +192,12 @@ struct KnownFPClass {
signBitMustBeZero();
}
+ static KnownFPClass fneg(const KnownFPClass &Src) {
+ KnownFPClass Known = Src;
+ Known.fneg();
+ return Known;
+ }
+
static KnownFPClass fabs(const KnownFPClass &Src) {
KnownFPClass Known = Src;
Known.fabs();
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
index 86c229c667eea..78f2aa4815df9 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
@@ -2147,6 +2147,16 @@ inferFastMathValueFlagsBinOp(FastMathFlags FMF, FPClassTest ValidResults,
return FMF;
}
+static FPClassTest adjustDemandedMaskFromFlags(FPClassTest DemandedMask,
+ FastMathFlags FMF) {
+ if (FMF.noNaNs())
+ DemandedMask &= ~fcNan;
+
+ if (FMF.noInfs())
+ DemandedMask &= ~fcInf;
+ return DemandedMask;
+}
+
Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Instruction *I,
FPClassTest DemandedMask,
KnownFPClass &Known,
@@ -2161,11 +2171,7 @@ Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Instruction *I,
FastMathFlags FMF;
if (auto *FPOp = dyn_cast<FPMathOperator>(I)) {
FMF = FPOp->getFastMathFlags();
- if (FMF.noNaNs())
- DemandedMask &= ~fcNan;
-
- if (FMF.noInfs())
- DemandedMask &= ~fcInf;
+ DemandedMask = adjustDemandedMaskFromFlags(DemandedMask, FMF);
}
switch (I->getOpcode()) {
@@ -2958,11 +2964,7 @@ Value *InstCombinerImpl::SimplifyMultipleUseDemandedFPClass(
FastMathFlags FMF;
if (auto *FPOp = dyn_cast<FPMathOperator>(I)) {
FMF = FPOp->getFastMathFlags();
- if (FMF.noNaNs())
- DemandedMask &= ~fcNan;
-
- if (FMF.noInfs())
- DemandedMask &= ~fcInf;
+ DemandedMask = adjustDemandedMaskFromFlags(DemandedMask, FMF);
}
switch (I->getOpcode()) {
@@ -2987,6 +2989,37 @@ Value *InstCombinerImpl::SimplifyMultipleUseDemandedFPClass(
Known = KnownLHS.intersectWith(KnownRHS);
break;
}
+ case Instruction::FNeg: {
+ // Special case fneg(fabs(x))
+ Value *Src;
+
+ Value *FNegSrc = I->getOperand(0);
+ if (!match(FNegSrc, m_FAbs(m_Value(Src)))) {
+ Known = computeKnownFPClass(I, DemandedMask, CxtI, Depth + 1);
+ break;
+ }
+
+ FastMathFlags FabsFMF = cast<FPMathOperator>(FNegSrc))->getFastMathFlags();
+ FPClassTest ThisDemandedMask =
+ adjustDemandedMaskFromFlags(DemandedMask, FabsFMF);
+
+ KnownFPClass KnownSrc =
+ computeKnownFPClass(Src, fcAllFlags, CxtI, Depth + 1);
+
+ if ((ThisDemandedMask & fcNan) == fcNone)
+ KnownSrc.knownNot(fcNan);
+ if ((ThisDemandedMask & fcInf) == fcNone)
+ KnownSrc.knownNot(fcInf);
+
+ // If the source value is known negative, we can directly fold to it.
+
+ // TODO: If the only sign bit difference is for 0, ignore it for nsz.
+ if (KnownSrc.SignBit == true)
+ return Src;
+
+ Known = KnownFPClass::fneg(KnownFPClass::fabs(KnownSrc));
+ break;
+ }
case Instruction::Call: {
const CallInst *CI = cast<CallInst>(I);
const Intrinsic::ID IID = CI->getIntrinsicID();
diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass.ll
index 714e4ff4398e2..c53cf037c8078 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass.ll
@@ -777,6 +777,175 @@ define nofpclass(snan) float @fabs_src_known_negative(float nofpclass(nan pinf p
ret float %fabs
}
+define nofpclass(snan) float @fneg_fabs_src_known_negative_multiple_uses(float nofpclass(nan pinf pnorm psub pzero) %always.negative, ptr %ptr) {
+; CHECK-LABEL: define nofpclass(snan) float @fneg_fabs_src_known_negative_multiple_uses
+; CHECK-SAME: (float nofpclass(nan pinf pzero psub pnorm) [[ALWAYS_NEGATIVE:%.*]], ptr [[PTR:%.*]]) {
+; CHECK-NEXT: [[FABS:%.*]] = call float @llvm.fabs.f32(float [[ALWAYS_NEGATIVE]])
+; CHECK-NEXT: [[FNEG_FABS:%.*]] = fneg float [[FABS]]
+; CHECK-NEXT: store float [[FNEG_FABS]], ptr [[PTR]], align 4
+; CHECK-NEXT: ret float [[ALWAYS_NEGATIVE]]
+;
+ %fabs = call float @llvm.fabs.f32(float %always.negative)
+ %fneg.fabs = fneg float %fabs
+ store float %fneg.fabs, ptr %ptr
+ ret float %fneg.fabs
+}
+
+define nofpclass(snan) float @fneg_fabs_src_known_positive_multiple_uses(float nofpclass(nan ninf nnorm nsub nzero) %always.positive, ptr %ptr) {
+; CHECK-LABEL: define nofpclass(snan) float @fneg_fabs_src_known_positive_multiple_uses
+; CHECK-SAME: (float nofpclass(nan ninf nzero nsub nnorm) [[ALWAYS_POSITIVE:%.*]], ptr [[PTR:%.*]]) {
+; CHECK-NEXT: [[FNEG_FABS:%.*]] = fneg float [[ALWAYS_POSITIVE]]
+; CHECK-NEXT: store float [[FNEG_FABS]], ptr [[PTR]], align 4
+; CHECK-NEXT: ret float [[FNEG_FABS]]
+;
+ %fabs = call float @llvm.fabs.f32(float %always.positive)
+ %fneg.fabs = fneg float %fabs
+ store float %fneg.fabs, ptr %ptr
+ ret float %fneg.fabs
+}
+
+define nofpclass(snan) float @fneg_fabs_src_known_negative_or_nan_multiple_uses(float nofpclass(pinf pnorm psub pzero) %always.negative.or.nan, ptr %ptr) {
+; CHECK-LABEL: define nofpclass(snan) float @fneg_fabs_src_known_negative_or_nan_multiple_uses
+; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ALWAYS_NEGATIVE_OR_NAN:%.*]], ptr [[PTR:%.*]]) {
+; CHECK-NEXT: [[FABS:%.*]] = call float @llvm.fabs.f32(float [[ALWAYS_NEGATIVE_OR_NAN]])
+; CHECK-NEXT: [[FNEG_FABS:%.*]] = fneg float [[FABS]]
+; CHECK-NEXT: store float [[FNEG_FABS]], ptr [[PTR]], align 4
+; CHECK-NEXT: ret float [[FNEG_FABS]]
+;
+ %fabs = call float @llvm.fabs.f32(float %always.negative.or.nan)
+ %fneg.fabs = fneg float %fabs
+ store float %fneg.fabs, ptr %ptr
+ ret float %fneg.fabs
+}
+
+define nofpclass(nan) float @ret_nonan_fneg_fabs_src_known_positive_or_nan_multiple_uses(float nofpclass(ninf nnorm nsub nzero) %always.positive.or.nan, ptr %ptr) {
+; CHECK-LABEL: define nofpclass(nan) float @ret_nonan_fneg_fabs_src_known_positive_or_nan_multiple_uses
+; CHECK-SAME: (float nofpclass(ninf nzero nsub nnorm) [[ALWAYS_POSITIVE_OR_NAN:%.*]], ptr [[PTR:%.*]]) {
+; CHECK-NEXT: [[FABS:%.*]] = call float @llvm.fabs.f32(float [[ALWAYS_POSITIVE_OR_NAN]])
+; CHECK-NEXT: [[FNEG_FABS:%.*]] = fneg float [[FABS]]
+; CHECK-NEXT: store float [[FNEG_FABS]], ptr [[PTR]], align 4
+; CHECK-NEXT: ret float [[FNEG_FABS]]
+;
+ %fabs = call float @llvm.fabs.f32(float %always.positive.or.nan)
+ %fneg.fabs = fneg float %fabs
+ store float %fneg.fabs, ptr %ptr
+ ret float %fneg.fabs
+}
+
+define nofpclass(snan) float @fneg_nnan_fabs_src_known_negative_or_nan_multiple_uses(float nofpclass(pinf pnorm psub pzero) %always.negative.or.nan, ptr %ptr) {
+; CHECK-LABEL: define nofpclass(snan) float @fneg_nnan_fabs_src_known_negative_or_nan_multiple_uses
+; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ALWAYS_NEGATIVE_OR_NAN:%.*]], ptr [[PTR:%.*]]) {
+; CHECK-NEXT: [[FABS:%.*]] = call float @llvm.fabs.f32(float [[ALWAYS_NEGATIVE_OR_NAN]])
+; CHECK-NEXT: [[FNEG_FABS:%.*]] = fneg nnan float [[FABS]]
+; CHECK-NEXT: store float [[FNEG_FABS]], ptr [[PTR]], align 4
+; CHECK-NEXT: ret float [[ALWAYS_NEGATIVE_OR_NAN]]
+;
+ %fabs = call float @llvm.fabs.f32(float %always.negative.or.nan)
+ %fneg.fabs = fneg nnan float %fabs
+ store float %fneg.fabs, ptr %ptr
+ ret float %fneg.fabs
+}
+
+define nofpclass(snan) float @fneg_fabs_nnan_src_known_negative_or_nan_multiple_uses(float nofpclass(pinf pnorm psub pzero) %always.negative.or.nan, ptr %ptr) {
+; CHECK-LABEL: define nofpclass(snan) float @fneg_fabs_nnan_src_known_negative_or_nan_multiple_uses
+; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ALWAYS_NEGATIVE_OR_NAN:%.*]], ptr [[PTR:%.*]]) {
+; CHECK-NEXT: [[FABS:%.*]] = call nnan float @llvm.fabs.f32(float [[ALWAYS_NEGATIVE_OR_NAN]])
+; CHECK-NEXT: [[FNEG_FABS:%.*]] = fneg float [[FABS]]
+; CHECK-NEXT: store float [[FNEG_FABS]], ptr [[PTR]], align 4
+; CHECK-NEXT: ret float [[ALWAYS_NEGATIVE_OR_NAN]]
+;
+ %fabs = call nnan float @llvm.fabs.f32(float %always.negative.or.nan)
+ %fneg.fabs = fneg float %fabs
+ store float %fneg.fabs, ptr %ptr
+ ret float %fneg.fabs
+}
+
+define nofpclass(snan) float @fneg_fabs_nofpclass_nan_src__known_negative_or_nan_multiple_uses(float nofpclass(pinf pnorm psub pzero) %always.negative.or.nan, ptr %ptr) {
+; CHECK-LABEL: define nofpclass(snan) float @fneg_fabs_nofpclass_nan_src__known_negative_or_nan_multiple_uses
+; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ALWAYS_NEGATIVE_OR_NAN:%.*]], ptr [[PTR:%.*]]) {
+; CHECK-NEXT: [[FABS:%.*]] = call float @llvm.fabs.f32(float nofpclass(nan) [[ALWAYS_NEGATIVE_OR_NAN]])
+; CHECK-NEXT: [[FNEG_FABS:%.*]] = fneg float [[FABS]]
+; CHECK-NEXT: store float [[FNEG_FABS]], ptr [[PTR]], align 4
+; CHECK-NEXT: ret float [[FNEG_FABS]]
+;
+ %fabs = call float @llvm.fabs.f32(float nofpclass(nan) %always.negative.or.nan)
+ %fneg.fabs = fneg float %fabs
+ store float %fneg.fabs, ptr %ptr
+ ret float %fneg.fabs
+}
+
+; Take no-posinf from ninf flag
+define nofpclass(snan) float @ret_fneg_ninf_fabs_src_known_negative_or_posinf_multiple_uses(float nofpclass(nan pnorm psub pzero) %negative.or.neginf, ptr %ptr) {
+; CHECK-LABEL: define nofpclass(snan) float @ret_fneg_ninf_fabs_src_known_negative_or_posinf_multiple_uses
+; CHECK-SAME: (float nofpclass(nan pzero psub pnorm) [[NEGATIVE_OR_NEGINF:%.*]], ptr [[PTR:%.*]]) {
+; CHECK-NEXT: [[FABS:%.*]] = call float @llvm.fabs.f32(float [[NEGATIVE_OR_NEGINF]])
+; CHECK-NEXT: [[FNEG_FABS:%.*]] = fneg ninf float [[FABS]]
+; CHECK-NEXT: store float [[FNEG_FABS]], ptr [[PTR]], align 4
+; CHECK-NEXT: ret float [[NEGATIVE_OR_NEGINF]]
+;
+ %fabs = call float @llvm.fabs.f32(float %negative.or.neginf)
+ %fneg.fabs = fneg ninf float %fabs
+ store float %fneg.fabs, ptr %ptr
+ ret float %fneg.fabs
+}
+
+; Take no-posinf from ninf flag
+define nofpclass(snan) float @ret_fneg_fabs_ninf_src_known_negative_or_posinf_multiple_uses(float nofpclass(nan pnorm psub pzero) %negative.or.neginf, ptr %ptr) {
+; CHECK-LABEL: define nofpclass(snan) float @ret_fneg_fabs_ninf_src_known_negative_or_posinf_multiple_uses
+; CHECK-SAME: (float nofpclass(nan pzero psub pnorm) [[NEGATIVE_OR_NEGINF:%.*]], ptr [[PTR:%.*]]) {
+; CHECK-NEXT: [[FABS:%.*]] = call ninf float @llvm.fabs.f32(float [[NEGATIVE_OR_NEGINF]])
+; CHECK-NEXT: [[FNEG_FABS:%.*]] = fneg float [[FABS]]
+; CHECK-NEXT: store float [[FNEG_FABS]], ptr [[PTR]], align 4
+; CHECK-NEXT: ret float [[NEGATIVE_OR_NEGINF]]
+;
+ %fabs = call ninf float @llvm.fabs.f32(float %negative.or.neginf)
+ %fneg.fabs = fneg float %fabs
+ store float %fneg.fabs, ptr %ptr
+ ret float %fneg.fabs
+}
+
+define nofpclass(snan) float @fneg_fabs_src_known_negative_or_poszero_multiple_uses(float nofpclass(nan pinf pnorm psub) %always.negative.or.pzero, ptr %ptr) {
+; CHECK-LABEL: define nofpclass(snan) float @fneg_fabs_src_known_negative_or_poszero_multiple_uses
+; CHECK-SAME: (float nofpclass(nan pinf psub pnorm) [[ALWAYS_NEGATIVE_OR_PZERO:%.*]], ptr [[PTR:%.*]]) {
+; CHECK-NEXT: [[FABS:%.*]] = call float @llvm.fabs.f32(float [[ALWAYS_NEGATIVE_OR_PZERO]])
+; CHECK-NEXT: [[FNEG_FABS:%.*]] = fneg float [[FABS]]
+; CHECK-NEXT: store float [[FNEG_FABS]], ptr [[PTR]], align 4
+; CHECK-NEXT: ret float [[FNEG_FABS]]
+;
+ %fabs = call float @llvm.fabs.f32(float %always.negative.or.pzero)
+ %fneg.fabs = fneg float %fabs
+ store float %fneg.fabs, ptr %ptr
+ ret float %fneg.fabs
+}
+
+define nofpclass(snan) float @fneg_fabs_nsz_src_known_negative_or_poszero_multiple_uses(float nofpclass(nan pinf pnorm psub) %always.negative.or.pzero, ptr %ptr) {
+; CHECK-LABEL: define nofpclass(snan) float @fneg_fabs_nsz_src_known_negative_or_poszero_multiple_uses
+; CHECK-SAME: (float nofpclass(nan pinf psub pnorm) [[ALWAYS_NEGATIVE_OR_PZERO:%.*]], ptr [[PTR:%.*]]) {
+; CHECK-NEXT: [[FABS:%.*]] = call nsz float @llvm.fabs.f32(float [[ALWAYS_NEGATIVE_OR_PZERO]])
+; CHECK-NEXT: [[FNEG_FABS:%.*]] = fneg float [[FABS]]
+; CHECK-NEXT: store float [[FNEG_FABS]], ptr [[PTR]], align 4
+; CHECK-NEXT: ret float [[FNEG_FABS]]
+;
+ %fabs = call nsz float @llvm.fabs.f32(float %always.negative.or.pzero)
+ %fneg.fabs = fneg float %fabs
+ store float %fneg.fabs, ptr %ptr
+ ret float %fneg.fabs
+}
+
+define nofpclass(snan) float @fneg_nsz_fabs_src_known_negative_or_poszero_multiple_uses(float nofpclass(nan pinf pnorm psub) %always.negative.or.pzero, ptr %ptr) {
+; CHECK-LABEL: define nofpclass(snan) float @fneg_nsz_fabs_src_known_negative_or_poszero_multiple_uses
+; CHECK-SAME: (float nofpclass(nan pinf psub pnorm) [[ALWAYS_NEGATIVE_OR_PZERO:%.*]], ptr [[PTR:%.*]]) {
+; CHECK-NEXT: [[FABS:%.*]] = call float @llvm.fabs.f32(float [[ALWAYS_NEGATIVE_OR_PZERO]])
+; CHECK-NEXT: [[FNEG_FABS:%.*]] = fneg nsz float [[FABS]]
+; CHECK-NEXT: store float [[FNEG_FABS]], ptr [[PTR]], align 4
+; CHECK-NEXT: ret float [[FNEG_FABS]]
+;
+ %fabs = call float @llvm.fabs.f32(float %always.negative.or.pzero)
+ %fneg.fabs = fneg nsz float %fabs
+ store float %fneg.fabs, ptr %ptr
+ ret float %fneg.fabs
+}
+
; should fold to ret copysign(%x)
define nofpclass(inf) float @ret_nofpclass_inf__copysign_unknown_select_pinf_rhs(i1 %cond, float %x, float %unknown.sign) {
; CHECK-LABEL: define nofpclass(inf) float @ret_nofpclass_inf__copysign_unknown_select_pinf_rhs
More information about the llvm-branch-commits
mailing list