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 <[email protected]> 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 _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
