simplifyBinaryIntrinsic: Return nan if snan is passed to maxnum/minnum#180105
Open
simplifyBinaryIntrinsic: Return nan if snan is passed to maxnum/minnum#180105
Conversation
Member
|
@llvm/pr-subscribers-llvm-transforms @llvm/pr-subscribers-backend-amdgpu Author: YunQiang Su (wzssyqa) ChangesFixes: #138303 3 Files Affected:
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:%.*]])
|
Member
|
@llvm/pr-subscribers-llvm-analysis Author: YunQiang Su (wzssyqa) ChangesFixes: #138303 3 Files Affected:
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:%.*]])
|
🐧 Linux x64 Test Results
✅ The build succeeded and all tests passed. |
🪟 Windows x64 Test Results
✅ The build succeeded and all tests passed. |
nikic
reviewed
Feb 6, 2026
Contributor
nikic
left a comment
There was a problem hiding this comment.
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes: #138303, #170486