Skip to content

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

Open
wzssyqa wants to merge 4 commits intollvm:mainfrom
wzssyqa:fmax-cse
Open

simplifyBinaryIntrinsic: Return nan if snan is passed to maxnum/minnum#180105
wzssyqa wants to merge 4 commits intollvm:mainfrom
wzssyqa:fmax-cse

Conversation

@wzssyqa
Copy link
Copy Markdown
Contributor

@wzssyqa wzssyqa commented Feb 6, 2026

Fixes: #138303, #170486

@wzssyqa wzssyqa requested a review from nikic as a code owner February 6, 2026 02:35
@wzssyqa wzssyqa requested a review from arsenm February 6, 2026 02:35
@llvmbot llvmbot added backend:AMDGPU llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:analysis Includes value tracking, cost tables and constant folding llvm:transforms labels Feb 6, 2026
@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Feb 6, 2026

@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-backend-amdgpu

Author: YunQiang Su (wzssyqa)

Changes

Fixes: #138303


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

3 Files Affected:

  • (modified) llvm/lib/Analysis/InstructionSimplify.cpp (+4-1)
  • (modified) llvm/test/CodeGen/AMDGPU/fcanonicalize-elimination.ll (+1-3)
  • (modified) llvm/test/Transforms/EarlyCSE/commute.ll (+61)
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 0e0c092271a38..a3e0994b72986 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -6668,6 +6668,7 @@ static MinMaxOptResult OptimizeConstMinMax(const Constant *RHSConst,
   assert(OutNewConstVal != nullptr);
 
   bool PropagateNaN = IID == Intrinsic::minimum || IID == Intrinsic::maximum;
+  bool PropagateNaN_S = IID == Intrinsic::minnum || IID == Intrinsic::maxnum;
   bool ReturnsOtherForAllNaNs =
       IID == Intrinsic::minimumnum || IID == Intrinsic::maximumnum;
   bool IsMin = IID == Intrinsic::minimum || IID == Intrinsic::minnum ||
@@ -6686,12 +6687,14 @@ static MinMaxOptResult OptimizeConstMinMax(const Constant *RHSConst,
 
   // minnum(x, qnan) -> x
   // maxnum(x, qnan) -> x
+  // minnum(x, snan) -> qnan
+  // maxnum(x, snan) -> qnan
   // minimum(X, nan) -> qnan
   // maximum(X, nan) -> qnan
   // minimumnum(X, nan) -> x
   // maximumnum(X, nan) -> x
   if (CAPF.isNaN()) {
-    if (PropagateNaN) {
+    if (PropagateNaN || (PropagateNaN_S && CAPF.isSignaling())) {
       *OutNewConstVal = ConstantFP::get(CFP->getType(), CAPF.makeQuiet());
       return MinMaxOptResult::UseNewConstVal;
     } else if (ReturnsOtherForAllNaNs || !CAPF.isSignaling()) {
diff --git a/llvm/test/CodeGen/AMDGPU/fcanonicalize-elimination.ll b/llvm/test/CodeGen/AMDGPU/fcanonicalize-elimination.ll
index a87570ef5d848..a2b02fd1e0c3f 100644
--- a/llvm/test/CodeGen/AMDGPU/fcanonicalize-elimination.ll
+++ b/llvm/test/CodeGen/AMDGPU/fcanonicalize-elimination.ll
@@ -500,9 +500,7 @@ define amdgpu_kernel void @test_fold_canonicalize_minnum_value_f32(ptr addrspace
 ; FIXME: Should there be more checks here? minnum with sNaN operand might get simplified away.
 
 ; GCN-LABEL: test_fold_canonicalize_sNaN_value_f32:
-; GCN: {{flat|global}}_load_dword [[LOAD:v[0-9]+]]
-; VI: v_mul_f32_e32 v{{[0-9]+}}, 1.0, [[LOAD]]
-; GFX9: v_max_f32_e32 v{{[0-9]+}}, [[LOAD]], [[LOAD]]
+; GCN: v_mov_b32_e32 v{{[0-9]+}}, 0x7fc00000
 define amdgpu_kernel void @test_fold_canonicalize_sNaN_value_f32(ptr addrspace(1) %arg) {
   %id = tail call i32 @llvm.amdgcn.workitem.id.x()
   %gep = getelementptr inbounds float, ptr addrspace(1) %arg, i32 %id
diff --git a/llvm/test/Transforms/EarlyCSE/commute.ll b/llvm/test/Transforms/EarlyCSE/commute.ll
index edafeccd3c8cc..62fae0f544812 100644
--- a/llvm/test/Transforms/EarlyCSE/commute.ll
+++ b/llvm/test/Transforms/EarlyCSE/commute.ll
@@ -830,6 +830,67 @@ 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> @maxnum_const_snan_v2f32(<2 x float> %a) {
+; CHECK-LABEL: @maxnum_const_snan_v2f32(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    ret <2 x float> splat (float 0x7FFC000000000000)
+;
+entry:
+  %r = tail call <2 x float> @llvm.maxnum.v2f32(<2 x float> %a, <2 x float> <float 0x7FF4000000000000, float 0x7FF4000000000000>)
+  ret <2 x float> %r
+}
+define <2 x float> @maxnum_const_qnan_v2f32(<2 x float> %a) {
+; CHECK-LABEL: @maxnum_const_qnan_v2f32(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
+;
+entry:
+  %r = tail call <2 x float> @llvm.maxnum.v2f32(<2 x float> %a, <2 x float> <float 0x7FF8000000000000, float 0x7FF8000000000000>)
+  ret <2 x float> %r
+}
+define <2 x float> @maxnum_const_mixednan_v2f32(<2 x float> %a) {
+; CHECK-LABEL: @maxnum_const_mixednan_v2f32(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[R:%.*]] = tail call <2 x float> @llvm.maxnum.v2f32(<2 x float> [[A:%.*]], <2 x float> <float 0x7FF8000000000000, float 0x7FF4000000000000>)
+; CHECK-NEXT:    ret <2 x float> [[R]]
+;
+entry:
+  %r = tail call <2 x float> @llvm.maxnum.v2f32(<2 x float> %a, <2 x float> <float 0x7FF8000000000000, float 0x7FF4000000000000>)
+  ret <2 x float> %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 Feb 6, 2026

@llvm/pr-subscribers-llvm-analysis

Author: YunQiang Su (wzssyqa)

Changes

Fixes: #138303


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

3 Files Affected:

  • (modified) llvm/lib/Analysis/InstructionSimplify.cpp (+4-1)
  • (modified) llvm/test/CodeGen/AMDGPU/fcanonicalize-elimination.ll (+1-3)
  • (modified) llvm/test/Transforms/EarlyCSE/commute.ll (+61)
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 0e0c092271a38..a3e0994b72986 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -6668,6 +6668,7 @@ static MinMaxOptResult OptimizeConstMinMax(const Constant *RHSConst,
   assert(OutNewConstVal != nullptr);
 
   bool PropagateNaN = IID == Intrinsic::minimum || IID == Intrinsic::maximum;
+  bool PropagateNaN_S = IID == Intrinsic::minnum || IID == Intrinsic::maxnum;
   bool ReturnsOtherForAllNaNs =
       IID == Intrinsic::minimumnum || IID == Intrinsic::maximumnum;
   bool IsMin = IID == Intrinsic::minimum || IID == Intrinsic::minnum ||
@@ -6686,12 +6687,14 @@ static MinMaxOptResult OptimizeConstMinMax(const Constant *RHSConst,
 
   // minnum(x, qnan) -> x
   // maxnum(x, qnan) -> x
+  // minnum(x, snan) -> qnan
+  // maxnum(x, snan) -> qnan
   // minimum(X, nan) -> qnan
   // maximum(X, nan) -> qnan
   // minimumnum(X, nan) -> x
   // maximumnum(X, nan) -> x
   if (CAPF.isNaN()) {
-    if (PropagateNaN) {
+    if (PropagateNaN || (PropagateNaN_S && CAPF.isSignaling())) {
       *OutNewConstVal = ConstantFP::get(CFP->getType(), CAPF.makeQuiet());
       return MinMaxOptResult::UseNewConstVal;
     } else if (ReturnsOtherForAllNaNs || !CAPF.isSignaling()) {
diff --git a/llvm/test/CodeGen/AMDGPU/fcanonicalize-elimination.ll b/llvm/test/CodeGen/AMDGPU/fcanonicalize-elimination.ll
index a87570ef5d848..a2b02fd1e0c3f 100644
--- a/llvm/test/CodeGen/AMDGPU/fcanonicalize-elimination.ll
+++ b/llvm/test/CodeGen/AMDGPU/fcanonicalize-elimination.ll
@@ -500,9 +500,7 @@ define amdgpu_kernel void @test_fold_canonicalize_minnum_value_f32(ptr addrspace
 ; FIXME: Should there be more checks here? minnum with sNaN operand might get simplified away.
 
 ; GCN-LABEL: test_fold_canonicalize_sNaN_value_f32:
-; GCN: {{flat|global}}_load_dword [[LOAD:v[0-9]+]]
-; VI: v_mul_f32_e32 v{{[0-9]+}}, 1.0, [[LOAD]]
-; GFX9: v_max_f32_e32 v{{[0-9]+}}, [[LOAD]], [[LOAD]]
+; GCN: v_mov_b32_e32 v{{[0-9]+}}, 0x7fc00000
 define amdgpu_kernel void @test_fold_canonicalize_sNaN_value_f32(ptr addrspace(1) %arg) {
   %id = tail call i32 @llvm.amdgcn.workitem.id.x()
   %gep = getelementptr inbounds float, ptr addrspace(1) %arg, i32 %id
diff --git a/llvm/test/Transforms/EarlyCSE/commute.ll b/llvm/test/Transforms/EarlyCSE/commute.ll
index edafeccd3c8cc..62fae0f544812 100644
--- a/llvm/test/Transforms/EarlyCSE/commute.ll
+++ b/llvm/test/Transforms/EarlyCSE/commute.ll
@@ -830,6 +830,67 @@ 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> @maxnum_const_snan_v2f32(<2 x float> %a) {
+; CHECK-LABEL: @maxnum_const_snan_v2f32(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    ret <2 x float> splat (float 0x7FFC000000000000)
+;
+entry:
+  %r = tail call <2 x float> @llvm.maxnum.v2f32(<2 x float> %a, <2 x float> <float 0x7FF4000000000000, float 0x7FF4000000000000>)
+  ret <2 x float> %r
+}
+define <2 x float> @maxnum_const_qnan_v2f32(<2 x float> %a) {
+; CHECK-LABEL: @maxnum_const_qnan_v2f32(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    ret <2 x float> [[A:%.*]]
+;
+entry:
+  %r = tail call <2 x float> @llvm.maxnum.v2f32(<2 x float> %a, <2 x float> <float 0x7FF8000000000000, float 0x7FF8000000000000>)
+  ret <2 x float> %r
+}
+define <2 x float> @maxnum_const_mixednan_v2f32(<2 x float> %a) {
+; CHECK-LABEL: @maxnum_const_mixednan_v2f32(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[R:%.*]] = tail call <2 x float> @llvm.maxnum.v2f32(<2 x float> [[A:%.*]], <2 x float> <float 0x7FF8000000000000, float 0x7FF4000000000000>)
+; CHECK-NEXT:    ret <2 x float> [[R]]
+;
+entry:
+  %r = tail call <2 x float> @llvm.maxnum.v2f32(<2 x float> %a, <2 x float> <float 0x7FF8000000000000, float 0x7FF4000000000000>)
+  ret <2 x float> %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:%.*]])

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 6, 2026

🐧 Linux x64 Test Results

  • 189754 tests passed
  • 5065 tests skipped

✅ The build succeeded and all tests passed.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 6, 2026

🪟 Windows x64 Test Results

  • 130601 tests passed
  • 2925 tests skipped

✅ The build succeeded and all tests passed.

Copy link
Copy Markdown
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

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

It would be good if alive2 were updated to the most recent semantics for minnum/maxnum before we start making changes in this area, so we can be confident in the correctness of changes.

I believe both the old and the new behavior proposed here are correct under the specified semantics, so this doesn't really "fix" anything, though I guess it may expose more folding opportunities to return a qNaN.

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

Labels

backend:AMDGPU llvm:analysis Includes value tracking, cost tables and constant folding llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[InstCombine] Formation of fmax ignores SNaN

3 participants