Skip to content

simplifyBinaryIntrinsic: Return nan if snan is passed maxnum/minnum#158470

Closed
wzssyqa wants to merge 1 commit intollvm:mainfrom
wzssyqa:fmax-cse
Closed

simplifyBinaryIntrinsic: Return nan if snan is passed maxnum/minnum#158470
wzssyqa wants to merge 1 commit intollvm:mainfrom
wzssyqa:fmax-cse

Conversation

@wzssyqa
Copy link
Copy Markdown
Contributor

@wzssyqa wzssyqa commented Sep 14, 2025

Fixes: #138303

@wzssyqa wzssyqa requested a review from nikic as a code owner September 14, 2025 08:59
@llvmbot llvmbot added llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:ir llvm:analysis Includes value tracking, cost tables and constant folding llvm:transforms labels Sep 14, 2025
@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Sep 14, 2025

@llvm/pr-subscribers-backend-amdgpu

@llvm/pr-subscribers-llvm-ir

Author: YunQiang Su (wzssyqa)

Changes

Fixes: #138303


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

3 Files Affected:

  • (modified) llvm/include/llvm/IR/PatternMatch.h (+4)
  • (modified) llvm/lib/Analysis/InstructionSimplify.cpp (+9-4)
  • (modified) llvm/test/Transforms/EarlyCSE/commute.ll (+33)
diff --git a/llvm/include/llvm/IR/PatternMatch.h b/llvm/include/llvm/IR/PatternMatch.h
index 2cb78904dd799..10cf451091198 100644
--- a/llvm/include/llvm/IR/PatternMatch.h
+++ b/llvm/include/llvm/IR/PatternMatch.h
@@ -707,9 +707,13 @@ m_SpecificInt_ICMP(ICmpInst::Predicate Predicate, const APInt &Threshold) {
 struct is_nan {
   bool isValue(const APFloat &C) const { return C.isNaN(); }
 };
+struct is_snan {
+  bool isValue(const APFloat &C) const { return C.isSignaling(); }
+};
 /// Match an arbitrary NaN constant. This includes quiet and signalling nans.
 /// For vectors, this includes constants with undefined elements.
 inline cstfp_pred_ty<is_nan> m_NaN() { return cstfp_pred_ty<is_nan>(); }
+inline cstfp_pred_ty<is_snan> m_SNaN() { return cstfp_pred_ty<is_snan>(); }
 
 struct is_nonnan {
   bool isValue(const APFloat &C) const { return !C.isNaN(); }
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 7bff13d59528c..cff8f6dfe8117 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -6735,12 +6735,17 @@ Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
     bool PropagateNaN = IID == Intrinsic::minimum || IID == Intrinsic::maximum;
     bool IsMin = IID == Intrinsic::minimum || IID == Intrinsic::minnum;
 
-    // minnum(X, nan) -> X
-    // maxnum(X, nan) -> X
+    // minnum(X, qnan) -> X
+    // maxnum(X, qnan) -> X
+    // minnum(X, snan) -> nan
+    // maxnum(X, snan) -> nan
     // minimum(X, nan) -> nan
     // maximum(X, nan) -> nan
-    if (match(Op1, m_NaN()))
-      return PropagateNaN ? propagateNaN(cast<Constant>(Op1)) : Op0;
+    if (match(Op1, m_NaN())) {
+      if (PropagateNaN || match(Op1, m_SNaN()))
+        return propagateNaN(cast<Constant>(Op1));
+      return Op0;
+    }
 
     // In the following folds, inf can be replaced with the largest finite
     // float, if the ninf flag is set.
diff --git a/llvm/test/Transforms/EarlyCSE/commute.ll b/llvm/test/Transforms/EarlyCSE/commute.ll
index edafeccd3c8cc..3e1f9abb8bd99 100644
--- a/llvm/test/Transforms/EarlyCSE/commute.ll
+++ b/llvm/test/Transforms/EarlyCSE/commute.ll
@@ -830,6 +830,39 @@ define float @maxnum(float %a, float %b) {
   ret float %r
 }
 
+define float @maxnum_const_snan(float %x) {
+; CHECK-LABEL: @maxnum_const_snan(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %r = call float @llvm.minnum.f32(float %x, float 0x7FF4000000000000)
+  ret float %r
+}
+
+define double @minnum_const_snan(double %x) {
+; CHECK-LABEL: @minnum_const_snan(
+; CHECK-NEXT:    ret double 0x7FFC000000000000
+;
+  %r = call double @llvm.minnum.f64(double %x, double 0x7FF4000000000000)
+  ret double %r
+}
+
+define float @maxnum_const_qnan(float %x) {
+; CHECK-LABEL: @maxnum_const_qnan(
+; CHECK-NEXT:    ret float [[X:%.*]]
+;
+  %r = call float @llvm.minnum.f32(float %x, float 0x7FF8000000000000)
+  ret float %r
+}
+
+define double @minnum_const_qnan(double %x) {
+; CHECK-LABEL: @minnum_const_qnan(
+; CHECK-NEXT:    ret double [[X:%.*]]
+;
+  %r = call double @llvm.minnum.f64(double %x, double 0x7FF8000000000000)
+  ret double %r
+}
+
+
 define <2 x float> @minnum(<2 x float> %a, <2 x float> %b) {
 ; CHECK-LABEL: @minnum(
 ; CHECK-NEXT:    [[X:%.*]] = call fast <2 x float> @llvm.minnum.v2f32(<2 x float> [[A:%.*]], <2 x float> [[B:%.*]])

@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Sep 14, 2025

@llvm/pr-subscribers-llvm-transforms

Author: YunQiang Su (wzssyqa)

Changes

Fixes: #138303


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

3 Files Affected:

  • (modified) llvm/include/llvm/IR/PatternMatch.h (+4)
  • (modified) llvm/lib/Analysis/InstructionSimplify.cpp (+9-4)
  • (modified) llvm/test/Transforms/EarlyCSE/commute.ll (+33)
diff --git a/llvm/include/llvm/IR/PatternMatch.h b/llvm/include/llvm/IR/PatternMatch.h
index 2cb78904dd799..10cf451091198 100644
--- a/llvm/include/llvm/IR/PatternMatch.h
+++ b/llvm/include/llvm/IR/PatternMatch.h
@@ -707,9 +707,13 @@ m_SpecificInt_ICMP(ICmpInst::Predicate Predicate, const APInt &Threshold) {
 struct is_nan {
   bool isValue(const APFloat &C) const { return C.isNaN(); }
 };
+struct is_snan {
+  bool isValue(const APFloat &C) const { return C.isSignaling(); }
+};
 /// Match an arbitrary NaN constant. This includes quiet and signalling nans.
 /// For vectors, this includes constants with undefined elements.
 inline cstfp_pred_ty<is_nan> m_NaN() { return cstfp_pred_ty<is_nan>(); }
+inline cstfp_pred_ty<is_snan> m_SNaN() { return cstfp_pred_ty<is_snan>(); }
 
 struct is_nonnan {
   bool isValue(const APFloat &C) const { return !C.isNaN(); }
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 7bff13d59528c..cff8f6dfe8117 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -6735,12 +6735,17 @@ Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
     bool PropagateNaN = IID == Intrinsic::minimum || IID == Intrinsic::maximum;
     bool IsMin = IID == Intrinsic::minimum || IID == Intrinsic::minnum;
 
-    // minnum(X, nan) -> X
-    // maxnum(X, nan) -> X
+    // minnum(X, qnan) -> X
+    // maxnum(X, qnan) -> X
+    // minnum(X, snan) -> nan
+    // maxnum(X, snan) -> nan
     // minimum(X, nan) -> nan
     // maximum(X, nan) -> nan
-    if (match(Op1, m_NaN()))
-      return PropagateNaN ? propagateNaN(cast<Constant>(Op1)) : Op0;
+    if (match(Op1, m_NaN())) {
+      if (PropagateNaN || match(Op1, m_SNaN()))
+        return propagateNaN(cast<Constant>(Op1));
+      return Op0;
+    }
 
     // In the following folds, inf can be replaced with the largest finite
     // float, if the ninf flag is set.
diff --git a/llvm/test/Transforms/EarlyCSE/commute.ll b/llvm/test/Transforms/EarlyCSE/commute.ll
index edafeccd3c8cc..3e1f9abb8bd99 100644
--- a/llvm/test/Transforms/EarlyCSE/commute.ll
+++ b/llvm/test/Transforms/EarlyCSE/commute.ll
@@ -830,6 +830,39 @@ define float @maxnum(float %a, float %b) {
   ret float %r
 }
 
+define float @maxnum_const_snan(float %x) {
+; CHECK-LABEL: @maxnum_const_snan(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %r = call float @llvm.minnum.f32(float %x, float 0x7FF4000000000000)
+  ret float %r
+}
+
+define double @minnum_const_snan(double %x) {
+; CHECK-LABEL: @minnum_const_snan(
+; CHECK-NEXT:    ret double 0x7FFC000000000000
+;
+  %r = call double @llvm.minnum.f64(double %x, double 0x7FF4000000000000)
+  ret double %r
+}
+
+define float @maxnum_const_qnan(float %x) {
+; CHECK-LABEL: @maxnum_const_qnan(
+; CHECK-NEXT:    ret float [[X:%.*]]
+;
+  %r = call float @llvm.minnum.f32(float %x, float 0x7FF8000000000000)
+  ret float %r
+}
+
+define double @minnum_const_qnan(double %x) {
+; CHECK-LABEL: @minnum_const_qnan(
+; CHECK-NEXT:    ret double [[X:%.*]]
+;
+  %r = call double @llvm.minnum.f64(double %x, double 0x7FF8000000000000)
+  ret double %r
+}
+
+
 define <2 x float> @minnum(<2 x float> %a, <2 x float> %b) {
 ; CHECK-LABEL: @minnum(
 ; CHECK-NEXT:    [[X:%.*]] = call fast <2 x float> @llvm.minnum.v2f32(<2 x float> [[A:%.*]], <2 x float> [[B:%.*]])

@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Sep 14, 2025

@llvm/pr-subscribers-llvm-analysis

Author: YunQiang Su (wzssyqa)

Changes

Fixes: #138303


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

3 Files Affected:

  • (modified) llvm/include/llvm/IR/PatternMatch.h (+4)
  • (modified) llvm/lib/Analysis/InstructionSimplify.cpp (+9-4)
  • (modified) llvm/test/Transforms/EarlyCSE/commute.ll (+33)
diff --git a/llvm/include/llvm/IR/PatternMatch.h b/llvm/include/llvm/IR/PatternMatch.h
index 2cb78904dd799..10cf451091198 100644
--- a/llvm/include/llvm/IR/PatternMatch.h
+++ b/llvm/include/llvm/IR/PatternMatch.h
@@ -707,9 +707,13 @@ m_SpecificInt_ICMP(ICmpInst::Predicate Predicate, const APInt &Threshold) {
 struct is_nan {
   bool isValue(const APFloat &C) const { return C.isNaN(); }
 };
+struct is_snan {
+  bool isValue(const APFloat &C) const { return C.isSignaling(); }
+};
 /// Match an arbitrary NaN constant. This includes quiet and signalling nans.
 /// For vectors, this includes constants with undefined elements.
 inline cstfp_pred_ty<is_nan> m_NaN() { return cstfp_pred_ty<is_nan>(); }
+inline cstfp_pred_ty<is_snan> m_SNaN() { return cstfp_pred_ty<is_snan>(); }
 
 struct is_nonnan {
   bool isValue(const APFloat &C) const { return !C.isNaN(); }
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 7bff13d59528c..cff8f6dfe8117 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -6735,12 +6735,17 @@ Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
     bool PropagateNaN = IID == Intrinsic::minimum || IID == Intrinsic::maximum;
     bool IsMin = IID == Intrinsic::minimum || IID == Intrinsic::minnum;
 
-    // minnum(X, nan) -> X
-    // maxnum(X, nan) -> X
+    // minnum(X, qnan) -> X
+    // maxnum(X, qnan) -> X
+    // minnum(X, snan) -> nan
+    // maxnum(X, snan) -> nan
     // minimum(X, nan) -> nan
     // maximum(X, nan) -> nan
-    if (match(Op1, m_NaN()))
-      return PropagateNaN ? propagateNaN(cast<Constant>(Op1)) : Op0;
+    if (match(Op1, m_NaN())) {
+      if (PropagateNaN || match(Op1, m_SNaN()))
+        return propagateNaN(cast<Constant>(Op1));
+      return Op0;
+    }
 
     // In the following folds, inf can be replaced with the largest finite
     // float, if the ninf flag is set.
diff --git a/llvm/test/Transforms/EarlyCSE/commute.ll b/llvm/test/Transforms/EarlyCSE/commute.ll
index edafeccd3c8cc..3e1f9abb8bd99 100644
--- a/llvm/test/Transforms/EarlyCSE/commute.ll
+++ b/llvm/test/Transforms/EarlyCSE/commute.ll
@@ -830,6 +830,39 @@ define float @maxnum(float %a, float %b) {
   ret float %r
 }
 
+define float @maxnum_const_snan(float %x) {
+; CHECK-LABEL: @maxnum_const_snan(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %r = call float @llvm.minnum.f32(float %x, float 0x7FF4000000000000)
+  ret float %r
+}
+
+define double @minnum_const_snan(double %x) {
+; CHECK-LABEL: @minnum_const_snan(
+; CHECK-NEXT:    ret double 0x7FFC000000000000
+;
+  %r = call double @llvm.minnum.f64(double %x, double 0x7FF4000000000000)
+  ret double %r
+}
+
+define float @maxnum_const_qnan(float %x) {
+; CHECK-LABEL: @maxnum_const_qnan(
+; CHECK-NEXT:    ret float [[X:%.*]]
+;
+  %r = call float @llvm.minnum.f32(float %x, float 0x7FF8000000000000)
+  ret float %r
+}
+
+define double @minnum_const_qnan(double %x) {
+; CHECK-LABEL: @minnum_const_qnan(
+; CHECK-NEXT:    ret double [[X:%.*]]
+;
+  %r = call double @llvm.minnum.f64(double %x, double 0x7FF8000000000000)
+  ret double %r
+}
+
+
 define <2 x float> @minnum(<2 x float> %a, <2 x float> %b) {
 ; CHECK-LABEL: @minnum(
 ; CHECK-NEXT:    [[X:%.*]] = call fast <2 x float> @llvm.minnum.v2f32(<2 x float> [[A:%.*]], <2 x float> [[B:%.*]])

@wzssyqa
Copy link
Copy Markdown
Contributor Author

wzssyqa commented Sep 28, 2025

@arsenm ping.

@arsenm arsenm added the floating-point Floating-point math label Oct 10, 2025
if (match(Op1, m_NaN()))
if (match(Op1, m_QNaN()))
return PropagateNaN ? propagateNaN(cast<Constant>(Op1)) : Op0;
else if (match(Op1, m_SNaN()))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No else after return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you add a todo about the mixed non-splat case

@wzssyqa
Copy link
Copy Markdown
Contributor Author

wzssyqa commented Dec 2, 2025

See #168838

@wzssyqa wzssyqa closed this Dec 2, 2025
@arsenm
Copy link
Copy Markdown
Contributor

arsenm commented Jan 30, 2026

Some version of this should be brought back

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend:AMDGPU 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:ir llvm:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[InstCombine] Formation of fmax ignores SNaN

3 participants