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:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes labels Dec 29, 2025 — with Graphite App
@arsenm arsenm marked this pull request as ready for review December 29, 2025 15:34
@arsenm arsenm requested a review from nikic as a code owner December 29, 2025 15:34
@llvmbot llvmbot added llvm:support llvm:analysis Includes value tracking, cost tables and constant folding llvm:transforms labels Dec 29, 2025
@llvmbot
Copy link
Member

llvmbot commented Dec 29, 2025

@llvm/pr-subscribers-llvm-support
@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-llvm-analysis

Author: Matt Arsenault (arsenm)

Changes

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

5 Files Affected:

  • (modified) llvm/include/llvm/Support/KnownFPClass.h (+29)
  • (modified) llvm/lib/Analysis/ValueTracking.cpp (+5-59)
  • (modified) llvm/lib/Support/KnownFPClass.cpp (+58)
  • (modified) llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp (+122-1)
  • (modified) llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fmul.ll (+46-39)
diff --git a/llvm/include/llvm/Support/KnownFPClass.h b/llvm/include/llvm/Support/KnownFPClass.h
index bf70ddb272e29..62df87ad8a67e 100644
--- a/llvm/include/llvm/Support/KnownFPClass.h
+++ b/llvm/include/llvm/Support/KnownFPClass.h
@@ -54,6 +54,9 @@ struct KnownFPClass {
   /// Return true if it's known this can never be an infinity.
   bool isKnownNeverInfinity() const { return isKnownNever(fcInf); }
 
+  /// Return true if it's known this can never be an infinity or nan
+  bool isKnownNeverInfOrNaN() const { return isKnownNever(fcInf | fcNan); }
+
   /// Return true if it's known this can never be +infinity.
   bool isKnownNeverPosInfinity() const { return isKnownNever(fcPosInf); }
 
@@ -119,6 +122,17 @@ struct KnownFPClass {
     return isKnownNever(OrderedGreaterThanZeroMask);
   }
 
+  /// Return true if it's know this can never be a negative value or a logical
+  /// 0.
+  ///
+  ///      NaN --> true
+  ///  x >= -0 --> false
+  ///     nsub --> true if mode is ieee, false otherwise.
+  ///   x < -0 --> true
+  bool cannotBeOrderedGreaterEqZero(DenormalMode Mode) const {
+    return isKnownNever(fcPositive) && isKnownNeverLogicalNegZero(Mode);
+  }
+
   KnownFPClass &operator|=(const KnownFPClass &RHS) {
     KnownFPClasses = KnownFPClasses | RHS.KnownFPClasses;
 
@@ -165,6 +179,21 @@ struct KnownFPClass {
   canonicalize(const KnownFPClass &Src,
                DenormalMode DenormMode = DenormalMode::getDynamic());
 
+  /// Report known values for fmul
+  LLVM_ABI static KnownFPClass
+  fmul(const KnownFPClass &LHS, const KnownFPClass &RHS,
+       DenormalMode Mode = DenormalMode::getDynamic());
+
+  // Special case of fmul x, x.
+  static KnownFPClass square(const KnownFPClass &Src,
+                             DenormalMode Mode = DenormalMode::getDynamic()) {
+    KnownFPClass Known = fmul(Src, Src, Mode);
+
+    // X, * X is always non-negative or a NaN.
+    Known.knownNot(fcNegative);
+    return Known;
+  }
+
   /// Report known values for exp, exp2 and exp10.
   LLVM_ABI static KnownFPClass exp(const KnownFPClass &Src);
 
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index cf7c6796f76c7..757bf28144371 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5704,66 +5704,12 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
     computeKnownFPClass(Op->getOperand(0), DemandedElts, fcAllFlags, KnownLHS,
                         Q, Depth + 1);
 
-    // xor sign bit.
-    if ((KnownLHS.isKnownNever(fcNegative) &&
-         KnownRHS.isKnownNever(fcNegative)) ||
-        (KnownLHS.isKnownNever(fcPositive) &&
-         KnownRHS.isKnownNever(fcPositive)))
-      Known.knownNot(fcNegative);
-
-    if ((KnownLHS.isKnownAlways(fcNegative | fcNan) &&
-         KnownRHS.isKnownNever(fcNegative)) ||
-        (KnownLHS.isKnownNever(fcNegative) &&
-         KnownRHS.isKnownAlways(fcNegative | fcNan)))
-      Known.knownNot(fcPositive);
-
-    // inf * anything => inf or nan
-    if (KnownLHS.isKnownAlways(fcInf | fcNan) ||
-        KnownRHS.isKnownAlways(fcInf | fcNan))
-      Known.knownNot(fcNormal | fcSubnormal | fcZero);
-
-    // 0 * anything => 0 or nan
-    if (KnownRHS.isKnownAlways(fcZero | fcNan) ||
-        KnownLHS.isKnownAlways(fcZero | fcNan))
-      Known.knownNot(fcNormal | fcSubnormal | fcInf);
-
-    // +/-0 * +/-inf = nan
-    if ((KnownLHS.isKnownAlways(fcZero | fcNan) &&
-         KnownRHS.isKnownAlways(fcInf | fcNan)) ||
-        (KnownLHS.isKnownAlways(fcInf | fcNan) &&
-         KnownRHS.isKnownAlways(fcZero | fcNan)))
-      Known.knownNot(~fcNan);
-
-    if (!KnownLHS.isKnownNeverNaN() || !KnownRHS.isKnownNeverNaN())
-      break;
-
-    if (KnownLHS.SignBit && KnownRHS.SignBit) {
-      if (*KnownLHS.SignBit == *KnownRHS.SignBit)
-        Known.signBitMustBeZero();
-      else
-        Known.signBitMustBeOne();
-    }
-
-    // If 0 * +/-inf produces NaN.
-    if (KnownLHS.isKnownNeverInfinity() && KnownRHS.isKnownNeverInfinity()) {
-      Known.knownNot(fcNan);
-      break;
-    }
-
     const Function *F = cast<Instruction>(Op)->getFunction();
-    if (!F)
-      break;
-
-    Type *OpTy = Op->getType()->getScalarType();
-    const fltSemantics &FltSem = OpTy->getFltSemantics();
-    DenormalMode Mode = F->getDenormalMode(FltSem);
-
-    if ((KnownRHS.isKnownNeverInfinity() ||
-         KnownLHS.isKnownNeverLogicalZero(Mode)) &&
-        (KnownLHS.isKnownNeverInfinity() ||
-         KnownRHS.isKnownNeverLogicalZero(Mode)))
-      Known.knownNot(fcNan);
-
+    DenormalMode Mode =
+        F ? F->getDenormalMode(
+                Op->getType()->getScalarType()->getFltSemantics())
+          : DenormalMode::getDynamic();
+    Known = KnownFPClass::fmul(KnownLHS, KnownRHS, Mode);
     break;
   }
   case Instruction::FDiv:
diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index 9ca040366b611..cfed2e5aaf5e0 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -141,6 +141,64 @@ KnownFPClass KnownFPClass::canonicalize(const KnownFPClass &KnownSrc,
   return Known;
 }
 
+KnownFPClass KnownFPClass::fmul(const KnownFPClass &KnownLHS,
+                                const KnownFPClass &KnownRHS,
+                                DenormalMode Mode) {
+  KnownFPClass Known;
+
+  // xor sign bit.
+  if ((KnownLHS.isKnownNever(fcNegative) &&
+       KnownRHS.isKnownNever(fcNegative)) ||
+      (KnownLHS.isKnownNever(fcPositive) && KnownRHS.isKnownNever(fcPositive)))
+    Known.knownNot(fcNegative);
+
+  if ((KnownLHS.isKnownAlways(fcNegative | fcNan) &&
+       KnownRHS.isKnownNever(fcNegative)) ||
+      (KnownLHS.isKnownNever(fcNegative) &&
+       KnownRHS.isKnownAlways(fcNegative | fcNan)))
+    Known.knownNot(fcPositive);
+
+  // inf * anything => inf or nan
+  if (KnownLHS.isKnownAlways(fcInf) || KnownRHS.isKnownAlways(fcInf))
+    Known.knownNot(fcNormal | fcSubnormal | fcZero);
+
+  // 0 * anything => 0 or nan
+  if (KnownRHS.isKnownAlways(fcZero | fcNan) ||
+      KnownLHS.isKnownAlways(fcZero | fcNan))
+    Known.knownNot(fcNormal | fcSubnormal | fcInf);
+
+  // +/-0 * +/-inf = nan
+  if ((KnownLHS.isKnownAlways(fcZero | fcNan) &&
+       KnownRHS.isKnownAlways(fcInf | fcNan)) ||
+      (KnownLHS.isKnownAlways(fcInf | fcNan) &&
+       KnownRHS.isKnownAlways(fcZero | fcNan)))
+    Known.knownNot(~fcNan);
+
+  if (!KnownLHS.isKnownNeverNaN() || !KnownRHS.isKnownNeverNaN())
+    return Known;
+
+  if (KnownLHS.SignBit && KnownRHS.SignBit) {
+    if (*KnownLHS.SignBit == *KnownRHS.SignBit)
+      Known.signBitMustBeZero();
+    else
+      Known.signBitMustBeOne();
+  }
+
+  // If 0 * +/-inf produces NaN.
+  if (KnownLHS.isKnownNeverInfinity() && KnownRHS.isKnownNeverInfinity()) {
+    Known.knownNot(fcNan);
+    return Known;
+  }
+
+  if ((KnownRHS.isKnownNeverInfinity() ||
+       KnownLHS.isKnownNeverLogicalZero(Mode)) &&
+      (KnownLHS.isKnownNeverInfinity() ||
+       KnownRHS.isKnownNeverLogicalZero(Mode)))
+    Known.knownNot(fcNan);
+
+  return Known;
+}
+
 KnownFPClass KnownFPClass::exp(const KnownFPClass &KnownSrc) {
   KnownFPClass Known;
   Known.knownNot(fcNegative);
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
index 519e321572072..1c997db8e85ad 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
@@ -2005,13 +2005,18 @@ Value *InstCombinerImpl::SimplifyDemandedVectorElts(Value *V,
 
 /// For floating-point classes that resolve to a single bit pattern, return that
 /// value.
-static Constant *getFPClassConstant(Type *Ty, FPClassTest Mask) {
+static Constant *getFPClassConstant(Type *Ty, FPClassTest Mask,
+                                    bool IsCanonicalizing = false) {
   if (Mask == fcNone)
     return PoisonValue::get(Ty);
 
   if (Mask == fcPosZero)
     return Constant::getNullValue(Ty);
 
+  // Turn any possible snans into quiet if we can.
+  if (Mask == fcNan && IsCanonicalizing)
+    return ConstantFP::getQNaN(Ty);
+
   // TODO: Support aggregate types that are allowed by FPMathOperator.
   if (Ty->isAggregateType())
     return nullptr;
@@ -2280,6 +2285,122 @@ Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Value *V,
     Known = KnownLHS | KnownRHS;
     break;
   }
+  case Instruction::FMul: {
+    KnownFPClass KnownLHS, KnownRHS;
+
+    Value *X = I->getOperand(0);
+    Value *Y = I->getOperand(1);
+
+    FPClassTest SrcDemandedMask = DemandedMask & (fcNan | fcZero | fcSubnormal);
+
+    if (DemandedMask & fcInf) {
+      // mul x, inf = inf
+      // mul large_x, large_y = inf
+      SrcDemandedMask |= fcSubnormal | fcNormal | fcInf;
+    }
+
+    if (DemandedMask & fcNan) {
+      // mul +/-inf, 0 => nan
+      SrcDemandedMask |= fcZero | fcInf;
+
+      // TODO: Mode check
+      // mul +/-inf, sub => nan if daz
+      SrcDemandedMask |= fcSubnormal;
+    }
+
+    if (X == Y) {
+      if (SimplifyDemandedFPClass(I, 0, SrcDemandedMask, KnownLHS, Depth + 1))
+        return I;
+      Type *EltTy = VTy->getScalarType();
+
+      DenormalMode Mode = F.getDenormalMode(EltTy->getFltSemantics());
+      Known = KnownFPClass::square(KnownLHS, Mode);
+
+      // Propagate known result to simplify edge case checks.
+      if ((DemandedMask & fcNan) == fcNone)
+        Known.knownNot(fcNan);
+      if ((DemandedMask & fcPosInf) == fcNone)
+        Known.knownNot(fcInf);
+
+      FPClassTest ValidResults = DemandedMask & Known.KnownFPClasses;
+      if (Constant *Folded =
+              getFPClassConstant(VTy, ValidResults, /*IsCanonicalizing=*/true))
+        return Folded;
+
+      if (Known.isKnownAlways(fcPosZero | fcPosInf | fcNan)) {
+        // We can skip the fabs if the source was already known positive.
+        if (KnownLHS.isKnownAlways(fcPositive))
+          return X;
+
+        // => fabs(x), in case this was a -inf or -0.
+        // Note: Dropping canonicalize.
+        IRBuilderBase::InsertPointGuard Guard(Builder);
+        Builder.SetInsertPoint(I);
+        Value *Fabs = Builder.CreateUnaryIntrinsic(Intrinsic::fabs, X);
+        Fabs->takeName(I);
+        return Fabs;
+      }
+
+      return nullptr;
+    }
+
+    if (SimplifyDemandedFPClass(I, 1, SrcDemandedMask, KnownRHS, Depth + 1) ||
+        SimplifyDemandedFPClass(I, 0, SrcDemandedMask, KnownLHS, Depth + 1))
+      return I;
+
+    // Propagate nnan-ness to sources to simplify source checks.
+    if ((DemandedMask & fcNan) == fcNone) {
+      KnownLHS.knownNot(fcNan);
+      KnownRHS.knownNot(fcNan);
+    }
+
+    // TODO: Apply knowledge of no-infinity returns to sources.
+
+    // TODO: Known -0, turn into copysign(y, fneg(x)) like visitFMul.
+    if (KnownLHS.isKnownNeverInfOrNaN() &&
+        KnownRHS.isKnownAlways(fcPosZero | fcNan)) {
+      // => copysign(+0, lhs)
+      // Note: Dropping canonicalize
+      Value *Copysign = Builder.CreateCopySign(Y, X);
+      Copysign->takeName(I);
+      return Copysign;
+    }
+
+    if (KnownLHS.isKnownAlways(fcPosZero | fcNan) &&
+        KnownRHS.isKnownNeverInfOrNaN()) {
+      // => copysign(+0, rhs)
+      // Note: Dropping canonicalize
+      Value *Copysign = Builder.CreateCopySign(X, Y);
+      Copysign->takeName(I);
+      return Copysign;
+    }
+
+    Type *EltTy = VTy->getScalarType();
+    DenormalMode Mode = F.getDenormalMode(EltTy->getFltSemantics());
+
+    if (KnownLHS.isKnownAlways(fcInf | fcNan) &&
+        (KnownRHS.isKnownNeverNaN() &&
+         KnownRHS.cannotBeOrderedGreaterEqZero(Mode))) {
+      // Note: Dropping canonicalize
+      Value *Neg = Builder.CreateFNeg(X);
+      Neg->takeName(I);
+      return Neg;
+    }
+
+    if (KnownRHS.isKnownAlways(fcInf | fcNan) &&
+        (KnownLHS.isKnownNeverNaN() &&
+         KnownLHS.cannotBeOrderedGreaterEqZero(Mode))) {
+      // Note: Dropping canonicalize
+      Value *Neg = Builder.CreateFNeg(Y);
+      Neg->takeName(I);
+      return Neg;
+    }
+
+    Known = KnownFPClass::fmul(KnownLHS, KnownRHS, Mode);
+
+    FPClassTest ValidResults = DemandedMask & Known.KnownFPClasses;
+    return getFPClassConstant(VTy, ValidResults, /*IsCanonicalizing=*/true);
+  }
   default:
     Known = computeKnownFPClass(I, ~DemandedMask, CxtI, Depth + 1);
     break;
diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fmul.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fmul.ll
index 594d5fbddb61e..503262a503a0d 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fmul.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fmul.ll
@@ -46,8 +46,7 @@ define nofpclass(inf) float @ret_nofpclass_inf__fmul_unknown_or_pinf(i1 %cond, f
 define nofpclass(pinf pnorm psub pzero) float @ret_only_negative_results_or_nan_square(float %x) {
 ; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_only_negative_results_or_nan_square(
 ; CHECK-SAME: float [[X:%.*]]) {
-; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X]], [[X]]
-; CHECK-NEXT:    ret float [[MUL]]
+; CHECK-NEXT:    ret float 0x7FF8000000000000
 ;
   %mul = fmul float %x, %x
   ret float %mul
@@ -85,8 +84,7 @@ define nofpclass(inf norm sub nan) float @ret_only_zero_results_square(float %x)
 define nofpclass(inf norm sub zero) float @ret_only_nan_results_square(float %x) {
 ; CHECK-LABEL: define nofpclass(inf zero sub norm) float @ret_only_nan_results_square(
 ; CHECK-SAME: float [[X:%.*]]) {
-; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X]], [[X]]
-; CHECK-NEXT:    ret float [[MUL]]
+; CHECK-NEXT:    ret float 0x7FF8000000000000
 ;
   %mul = fmul float %x, %x
   ret float %mul
@@ -163,8 +161,7 @@ define nofpclass(pinf norm sub zero nan) float @ret_only_ninf_results_square(flo
 define nofpclass(inf) float @ret_src_must_be_zero_square(float nofpclass(nan inf norm sub) %x) {
 ; CHECK-LABEL: define nofpclass(inf) float @ret_src_must_be_zero_square(
 ; CHECK-SAME: float nofpclass(nan inf sub norm) [[X:%.*]]) {
-; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X]], [[X]]
-; CHECK-NEXT:    ret float [[MUL]]
+; CHECK-NEXT:    ret float 0.000000e+00
 ;
   %mul = fmul float %x, %x
   ret float %mul
@@ -173,8 +170,7 @@ define nofpclass(inf) float @ret_src_must_be_zero_square(float nofpclass(nan inf
 define nofpclass(inf) float @ret_src_must_be_pzero(float nofpclass(nan inf norm sub nzero) %x) {
 ; CHECK-LABEL: define nofpclass(inf) float @ret_src_must_be_pzero(
 ; CHECK-SAME: float nofpclass(nan inf nzero sub norm) [[X:%.*]]) {
-; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X]], [[X]]
-; CHECK-NEXT:    ret float [[MUL]]
+; CHECK-NEXT:    ret float 0.000000e+00
 ;
   %mul = fmul float %x, %x
   ret float %mul
@@ -183,8 +179,7 @@ define nofpclass(inf) float @ret_src_must_be_pzero(float nofpclass(nan inf norm
 define nofpclass(inf) float @ret_src_must_be_nzero(float nofpclass(nan inf norm sub pzero) %x) {
 ; CHECK-LABEL: define nofpclass(inf) float @ret_src_must_be_nzero(
 ; CHECK-SAME: float nofpclass(nan inf pzero sub norm) [[X:%.*]]) {
-; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X]], [[X]]
-; CHECK-NEXT:    ret float [[MUL]]
+; CHECK-NEXT:    ret float 0.000000e+00
 ;
   %mul = fmul float %x, %x
   ret float %mul
@@ -193,7 +188,7 @@ define nofpclass(inf) float @ret_src_must_be_nzero(float nofpclass(nan inf norm
 define nofpclass(inf) float @ret_src_must_be_zero_or_nan_square(float nofpclass(inf norm sub) %x) {
 ; CHECK-LABEL: define nofpclass(inf) float @ret_src_must_be_zero_or_nan_square(
 ; CHECK-SAME: float nofpclass(inf sub norm) [[X:%.*]]) {
-; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X]], [[X]]
+; CHECK-NEXT:    [[MUL:%.*]] = call float @llvm.fabs.f32(float [[X]])
 ; CHECK-NEXT:    ret float [[MUL]]
 ;
   %mul = fmul float %x, %x
@@ -204,6 +199,27 @@ define nofpclass(inf) float @ret_src_must_be_zero_or_nan_square(float nofpclass(
 define nofpclass(nzero) float @ret_src_must_be_nan_square(float nofpclass(inf norm sub zero) %x) {
 ; CHECK-LABEL: define nofpclass(nzero) float @ret_src_must_be_nan_square(
 ; CHECK-SAME: float nofpclass(inf zero sub norm) [[X:%.*]]) {
+; CHECK-NEXT:    ret float 0x7FF8000000000000
+;
+  %mul = fmul float %x, %x
+  ret float %mul
+}
+
+; Make sure this doesn't get dropped as a no-op
+define nofpclass(nzero) float @ret_src_must_be_positive_square(float nofpclass(ninf nnorm nsub nzero) %x) {
+; CHECK-LABEL: define nofpclass(nzero) float @ret_src_must_be_positive_square(
+; CHECK-SAME: float nofpclass(ninf nzero nsub nnorm) [[X:%.*]]) {
+; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X]], [[X]]
+; CHECK-NEXT:    ret float [[MUL]]
+;
+  %mul = fmul float %x, %x
+  ret float %mul
+}
+
+; Make sure this doesn't get dropped as a no-op
+define nofpclass(nzero) float @ret_src_must_be_negative_square(float nofpclass(pinf pnorm psub pzero) %x) {
+; CHECK-LABEL: define nofpclass(nzero) float @ret_src_must_be_negative_square(
+; CHECK-SAME: float nofpclass(pinf pzero psub pnorm) [[X:%.*]]) {
 ; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X]], [[X]]
 ; CHECK-NEXT:    ret float [[MUL]]
 ;
@@ -215,9 +231,7 @@ define nofpclass(nzero) float @ret_src_must_be_nan_square(float nofpclass(inf no
 define nofpclass(pinf pnorm psub pzero) float @ret_only_negative_results_or_nan_fabs_xy(float %x, float nofpclass(ninf nnorm nsub nzero) %y.pos.or.nan) {
 ; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_only_negative_results_or_nan_fabs_xy(
 ; CHECK-SAME: float [[X:%.*]], float nofpclass(ninf nzero nsub nnorm) [[Y_POS_OR_NAN:%.*]]) {
-; CHECK-NEXT:    [[X_FABS:%.*]] = call float @llvm.fabs.f32(float [[X]])
-; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X_FABS]], [[Y_POS_OR_NAN]]
-; CHECK-NEXT:    ret float [[MUL]]
+; CHECK-NEXT:    ret float 0x7FF8000000000000
 ;
   %x.fabs = call float @llvm.fabs.f32(float %x)
   %mul = fmul float %x.fabs, %y.pos.or.nan
@@ -228,9 +242,7 @@ define nofpclass(pinf pnorm psub pzero) float @ret_only_negative_results_or_nan_
 define nofpclass(pinf pnorm psub pzero nan) float @ret_only_negative_results_fabs_xy(float %x,float nofpclass(ninf nnorm nsub nzero) %y.pos.or.nan) {
 ; CHECK-LABEL: define nofpclass(nan pinf pzero psub pnorm) float @ret_only_negative_results_fabs_xy(
 ; CHECK-SAME: float [[X:%.*]], float nofpclass(ninf nzero nsub nnorm) [[Y_POS_OR_NAN:%.*]]) {
-; CHECK-NEXT:    [[X_FABS:%.*]] = call float @llvm.fabs.f32(float [[X]])
-; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X_FABS]], [[Y_POS_OR_NAN]]
-; CHECK-NEXT:    ret float [[MUL]]
+; CHECK-NEXT:    ret float poison
 ;
   %x.fabs = call float @llvm.fabs.f32(float %x)
   %mul = fmul float %x.fabs, %y.pos.or.nan
@@ -351,8 +363,7 @@ define nofpclass(pinf nan) float @ret_no_pinf_or_nan_results__lhs_known_non_inf(
 define nofpclass(inf nan) float @ret_no_inf_or_nan_results__lhs_known_non_inf(i1 %cond, float %x, float nofpclass(inf) %y) {
 ; CHECK-LABEL: define nofpclass(nan inf) float @ret_no_inf_or_nan_results__lhs_known_non_inf(
 ; CHECK-SAME: i1 [[COND:%.*]], float [[X:%.*]], float nofpclass(inf) [[Y:%.*]]) {
-; CHECK-NEXT:    [[X_OR_PINF:%.*]] = select i1 [[COND]], float [[X]], float 0x7FF0000000000000
-; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X_OR_PINF]], [[Y]]
+; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X]], [[Y]]
 ; CHECK-NEXT:    ret float [[MUL]]
 ;
   %x.or.pinf = select i1 %cond, float %x, float 0x7FF0000000000000
@@ -364,8 +375,7 @@ define nofpclass(inf nan) float @ret_no_inf_or_nan_results__lhs_known_non_inf(i1
 define nofpclass(inf nan) float @ret_no_inf_or_nan_results__rhs_known_non_inf(i1 %cond, float %x, float nofpclass(inf) %y) {
 ; CHECK-LABEL: define nofpclass(nan inf) float @ret_no_inf_or_nan_results__rhs_known_non_inf(
 ; CHECK-SAME: i1 [[COND:%.*]], float [[X:%.*]], float nofpclass(inf) [[Y:%.*]]) {
-; CHECK-NEXT:    [[Y_OR_PINF:%.*]] = select i1 [[COND]], float [[Y]], float 0x7FF0000000000000
-; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X]], [[Y_OR_PINF]]
+; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X]], [[Y]]
 ; CHECK-NEXT:    ret float [[MUL]]
 ;
   %y.or.pinf = select i1 %cond, float %y, float 0x7FF0000000000000
@@ -377,8 +387,7 @@ define nofpclass(inf nan) float @ret_no_inf_or_nan_results__rhs_known_non_inf(i1
 define nofpclass(ninf nnorm nsub nzero) float @ret_only_positive_results_or_nan_known_negative_fmul(float nofpclass(ninf nnorm nsub nzero) %only.positive.or.nan, float nofpclass(pinf pnorm psub pzero) %only.negative.or.nan) {
 ; CHECK-LABEL: define nofpclass...
[truncated]

@github-actions
Copy link

github-actions bot commented Dec 29, 2025

🪟 Windows x64 Test Results

  • 129051 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

  • 188029 tests passed
  • 4988 tests skipped

✅ The build succeeded and all tests passed.

@arsenm arsenm force-pushed the users/arsenm/instcombine/simplify-demanded-fpclass-fmul branch from 83c4abd to 2302c6a Compare December 30, 2025 18:41
@arsenm arsenm force-pushed the users/arsenm/instcombine/add-baseline-tests-simplifydemandedfpclass-fmul branch from 28318a0 to 5b8b491 Compare December 30, 2025 18:41
@github-actions
Copy link

github-actions bot commented Dec 30, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@arsenm arsenm force-pushed the users/arsenm/instcombine/add-baseline-tests-simplifydemandedfpclass-fmul branch from 5b8b491 to daeaccd Compare December 30, 2025 18:46
@arsenm arsenm force-pushed the users/arsenm/instcombine/simplify-demanded-fpclass-fmul branch from 2302c6a to bb4c18d Compare December 30, 2025 18:46
@arsenm arsenm force-pushed the users/arsenm/instcombine/add-baseline-tests-simplifydemandedfpclass-fmul branch from daeaccd to 75fc190 Compare December 30, 2025 21:46
@arsenm arsenm force-pushed the users/arsenm/instcombine/simplify-demanded-fpclass-fmul branch from bb4c18d to 171dd91 Compare December 30, 2025 21:46
@arsenm arsenm force-pushed the users/arsenm/instcombine/simplify-demanded-fpclass-fmul branch from 171dd91 to f2f9302 Compare December 31, 2025 09:45
@arsenm arsenm force-pushed the users/arsenm/instcombine/add-baseline-tests-simplifydemandedfpclass-fmul branch from 75fc190 to 8f68e52 Compare December 31, 2025 09:45

if (X == Y) {
if (SimplifyDemandedFPClass(I, 0, SrcDemandedMask, KnownLHS, Depth + 1))
return I;
Copy link
Member

Choose a reason for hiding this comment

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

It is a bit weird that the RHS doesn't get simplified as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this is kind of broken. It works out in these tests since the only select simplifications performed can support multiple uses. Really this needs to be broken up into single and multiple use versions like SimpilfyDemandedBits is

getFPClassConstant(VTy, ValidResults, /*IsCanonicalizing=*/true))
return Folded;

if (Known.isKnownAlways(fcPosZero | fcPosInf | fcNan)) {
Copy link
Member

Choose a reason for hiding this comment

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

The square of a large normal value can produce +inf. But it doesn't happen in the KnownFPClass propagation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is checking the result, which should assume inf is a possible result for normal inputs, so everything here seems to work as intended. I'll add an assert that the source isn't normal

@arsenm arsenm force-pushed the users/arsenm/instcombine/add-baseline-tests-simplifydemandedfpclass-fmul branch from 8f68e52 to dac5083 Compare January 5, 2026 10:04
@arsenm arsenm force-pushed the users/arsenm/instcombine/simplify-demanded-fpclass-fmul branch from f2f9302 to ec17cc4 Compare January 5, 2026 10:04
Base automatically changed from users/arsenm/instcombine/add-baseline-tests-simplifydemandedfpclass-fmul to main January 6, 2026 08:12
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: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