Skip to content

[ConstantFolding] Fold maximumnum and minimumnum #138700

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 8, 2025

Conversation

LewisCrawford
Copy link
Contributor

@LewisCrawford LewisCrawford commented May 6, 2025

Add constant-folding support for the maximumnum and minimumnum intrinsics, and extend the tests to show the qnan vs snan behavior differences between maxnum/maximum/maximumnum.

@llvmbot
Copy link
Member

llvmbot commented May 6, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Lewis Crawford (LewisCrawford)

Changes

Add constant-folding support for the fmaximumnum and fminimumnum intrinsics, and extend the tests to show the qnan vs snan behavior differences between maxnum/maximum/maximumnum.


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

3 Files Affected:

  • (modified) llvm/lib/Analysis/ConstantFolding.cpp (+8)
  • (modified) llvm/test/Transforms/InstSimplify/ConstProp/fp-undef.ll (+34)
  • (modified) llvm/test/Transforms/InstSimplify/ConstProp/min-max.ll (+326)
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index 5b329e2f898f3..412a0e8979193 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -1642,6 +1642,8 @@ bool llvm::canConstantFoldCallTo(const CallBase *Call, const Function *F) {
   case Intrinsic::maxnum:
   case Intrinsic::minimum:
   case Intrinsic::maximum:
+  case Intrinsic::minimumnum:
+  case Intrinsic::maximumnum:
   case Intrinsic::log:
   case Intrinsic::log2:
   case Intrinsic::log10:
@@ -2930,6 +2932,8 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
     case Intrinsic::minnum:
     case Intrinsic::maximum:
     case Intrinsic::minimum:
+    case Intrinsic::maximumnum:
+    case Intrinsic::minimumnum:
     case Intrinsic::nvvm_fmax_d:
     case Intrinsic::nvvm_fmin_d:
       // If one argument is undef, return the other argument.
@@ -3030,6 +3034,10 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
         return ConstantFP::get(Ty->getContext(), minimum(Op1V, Op2V));
       case Intrinsic::maximum:
         return ConstantFP::get(Ty->getContext(), maximum(Op1V, Op2V));
+      case Intrinsic::minimumnum:
+        return ConstantFP::get(Ty->getContext(), minimumnum(Op1V, Op2V));
+      case Intrinsic::maximumnum:
+        return ConstantFP::get(Ty->getContext(), maximumnum(Op1V, Op2V));
 
       case Intrinsic::nvvm_fmax_d:
       case Intrinsic::nvvm_fmax_f:
diff --git a/llvm/test/Transforms/InstSimplify/ConstProp/fp-undef.ll b/llvm/test/Transforms/InstSimplify/ConstProp/fp-undef.ll
index 6fa57a8a1467a..51988583ab1ac 100644
--- a/llvm/test/Transforms/InstSimplify/ConstProp/fp-undef.ll
+++ b/llvm/test/Transforms/InstSimplify/ConstProp/fp-undef.ll
@@ -5,6 +5,8 @@ declare <2 x double> @llvm.minnum.v2f64(<2 x double>, <2 x double>)
 declare <2 x double> @llvm.maxnum.v2f64(<2 x double>, <2 x double>)
 declare <2 x double> @llvm.minimum.v2f64(<2 x double>, <2 x double>)
 declare <2 x double> @llvm.maximum.v2f64(<2 x double>, <2 x double>)
+declare <2 x double> @llvm.minimumnum.v2f64(<2 x double>, <2 x double>)
+declare <2 x double> @llvm.maximumnum.v2f64(<2 x double>, <2 x double>)
 
 ; Constant folding - undef undef.
 
@@ -538,6 +540,38 @@ define <2 x double> @frem_undef_op0_constant_vec(<2 x double> %x) {
   ret <2 x double> %r
 }
 
+define <2 x double> @maximumnum_nan_op0_vec_partial_undef_op1_undef(<2 x double> %x) {
+; CHECK-LABEL: @maximumnum_nan_op0_vec_partial_undef_op1_undef(
+; CHECK-NEXT:    ret <2 x double> <double 0x7FF8000000000000, double undef>
+;
+  %r = call <2 x double> @llvm.maximumnum.v2f64(<2 x double> <double 0x7ff8000000000000, double undef>, <2 x double> undef)
+  ret <2 x double> %r
+}
+
+define <2 x double> @maximumnum_nan_op1_vec_partial_undef_op0_undef(<2 x double> %x) {
+; CHECK-LABEL: @maximumnum_nan_op1_vec_partial_undef_op0_undef(
+; CHECK-NEXT:    ret <2 x double> <double 0x7FF8000000000000, double undef>
+;
+  %r = call <2 x double> @llvm.maximumnum.v2f64(<2 x double> undef, <2 x double> <double 0x7ff8000000000000, double undef>)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minimumnum_nan_op0_vec_partial_undef_op1_undef(<2 x double> %x) {
+; CHECK-LABEL: @minimumnum_nan_op0_vec_partial_undef_op1_undef(
+; CHECK-NEXT:    ret <2 x double> <double 0x7FF8000000000000, double undef>
+;
+  %r = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> <double 0x7ff8000000000000, double undef>, <2 x double> undef)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minimumnum_nan_op1_vec_partial_undef_op0_undef(<2 x double> %x) {
+; CHECK-LABEL: @minimumnum_nan_op1_vec_partial_undef_op0_undef(
+; CHECK-NEXT:    ret <2 x double> <double 0x7FF8000000000000, double undef>
+;
+  %r = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> undef, <2 x double> <double 0x7ff8000000000000, double undef>)
+  ret <2 x double> %r
+}
+
 define <2 x double> @maximum_nan_op0_vec_partial_undef_op1_undef(<2 x double> %x) {
 ; CHECK-LABEL: @maximum_nan_op0_vec_partial_undef_op1_undef(
 ; CHECK-NEXT:    ret <2 x double> <double 0x7FF8000000000000, double undef>
diff --git a/llvm/test/Transforms/InstSimplify/ConstProp/min-max.ll b/llvm/test/Transforms/InstSimplify/ConstProp/min-max.ll
index 9120649eb5c4f..a633d29179896 100644
--- a/llvm/test/Transforms/InstSimplify/ConstProp/min-max.ll
+++ b/llvm/test/Transforms/InstSimplify/ConstProp/min-max.ll
@@ -29,6 +29,20 @@ declare <4 x float> @llvm.maximum.v4f32(<4 x float>, <4 x float>)
 declare <4 x bfloat> @llvm.maximum.v4bf16(<4 x bfloat>, <4 x bfloat>)
 declare <4 x half> @llvm.maximum.v4f16(<4 x half>, <4 x half>)
 
+declare float @llvm.minimumnum.f32(float, float)
+declare bfloat @llvm.minimumnum.bf16(bfloat, bfloat)
+declare half @llvm.minimumnum.f16(half, half)
+declare <4 x float> @llvm.minimumnum.v4f32(<4 x float>, <4 x float>)
+declare <4 x bfloat> @llvm.minimumnum.v4bf16(<4 x bfloat>, <4 x bfloat>)
+declare <4 x half> @llvm.minimumnum.v4f16(<4 x half>, <4 x half>)
+
+declare float @llvm.maximumnum.f32(float, float)
+declare bfloat @llvm.maximumnum.bf16(bfloat, bfloat)
+declare half @llvm.maximumnum.f16(half, half)
+declare <4 x float> @llvm.maximumnum.v4f32(<4 x float>, <4 x float>)
+declare <4 x bfloat> @llvm.maximumnum.v4bf16(<4 x bfloat>, <4 x bfloat>)
+declare <4 x half> @llvm.maximumnum.v4f16(<4 x half>, <4 x half>)
+
 declare i8 @llvm.smax.i8(i8, i8)
 declare <5 x i8> @llvm.smax.v5i8(<5 x i8>, <5 x i8>)
 
@@ -81,6 +95,22 @@ define float @minnum_float_qnan_p0() {
   ret float %min
 }
 
+define float @minnum_float_p0_snan() {
+; CHECK-LABEL: @minnum_float_p0_snan(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %min = call float @llvm.minnum.f32(float 0.0, float 0x7FF4000000000000)
+  ret float %min
+}
+
+define float @minnum_float_snan_p0() {
+; CHECK-LABEL: @minnum_float_snan_p0(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %min = call float @llvm.minnum.f32(float 0x7FF4000000000000, float 0.0)
+  ret float %min
+}
+
 define bfloat @minnum_bfloat() {
 ; CHECK-LABEL: @minnum_bfloat(
 ; CHECK-NEXT:    ret bfloat 0xR40A0
@@ -173,6 +203,22 @@ define float @maxnum_float_qnan_p0() {
   ret float %max
 }
 
+define float @maxnum_float_p0_snan() {
+; CHECK-LABEL: @maxnum_float_p0_snan(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %max = call float @llvm.maxnum.f32(float 0.0, float 0x7FF4000000000000)
+  ret float %max
+}
+
+define float @maxnum_float_snan_p0() {
+; CHECK-LABEL: @maxnum_float_snan_p0(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %max = call float @llvm.maxnum.f32(float 0x7FF4000000000000, float 0.0)
+  ret float %max
+}
+
 define bfloat @maxnum_bfloat() {
 ; CHECK-LABEL: @maxnum_bfloat(
 ; CHECK-NEXT:    ret bfloat 0xR4228
@@ -249,6 +295,38 @@ define half @minimum_half() {
   ret half %1
 }
 
+define float @minimum_float_p0_qnan() {
+; CHECK-LABEL: @minimum_float_p0_qnan(
+; CHECK-NEXT:    ret float 0x7FF8000000000000
+;
+  %max = call float @llvm.minimum.f32(float 0.0, float 0x7FF8000000000000)
+  ret float %max
+}
+
+define float @minimum_float_qnan_p0() {
+; CHECK-LABEL: @minimum_float_qnan_p0(
+; CHECK-NEXT:    ret float 0x7FF8000000000000
+;
+  %max = call float @llvm.minimum.f32(float 0x7FF8000000000000, float 0.0)
+  ret float %max
+}
+
+define float @minimum_float_p0_snan() {
+; CHECK-LABEL: @minimum_float_p0_snan(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %max = call float @llvm.minimum.f32(float 0.0, float 0x7FF4000000000000)
+  ret float %max
+}
+
+define float @minimum_float_snan_p0() {
+; CHECK-LABEL: @minimum_float_snan_p0(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %max = call float @llvm.minimum.f32(float 0x7FF4000000000000, float 0.0)
+  ret float %max
+}
+
 ; Check that minimum propagates its NaN or smaller argument
 
 define <4 x float> @minimum_float_vec() {
@@ -309,6 +387,38 @@ define half @maximum_half() {
   ret half %1
 }
 
+define float @maximum_float_p0_qnan() {
+; CHECK-LABEL: @maximum_float_p0_qnan(
+; CHECK-NEXT:    ret float 0x7FF8000000000000
+;
+  %max = call float @llvm.maximum.f32(float 0.0, float 0x7FF8000000000000)
+  ret float %max
+}
+
+define float @maximum_float_qnan_p0() {
+; CHECK-LABEL: @maximum_float_qnan_p0(
+; CHECK-NEXT:    ret float 0x7FF8000000000000
+;
+  %max = call float @llvm.maximum.f32(float 0x7FF8000000000000, float 0.0)
+  ret float %max
+}
+
+define float @maximum_float_p0_snan() {
+; CHECK-LABEL: @maximum_float_p0_snan(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %max = call float @llvm.maximum.f32(float 0.0, float 0x7FF4000000000000)
+  ret float %max
+}
+
+define float @maximum_float_snan_p0() {
+; CHECK-LABEL: @maximum_float_snan_p0(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %max = call float @llvm.maximum.f32(float 0x7FF4000000000000, float 0.0)
+  ret float %max
+}
+
 ; Check that maximum propagates its NaN or greater argument
 
 define <4 x float> @maximum_float_vec() {
@@ -345,6 +455,222 @@ define <4 x float> @maximum_float_zeros_vec() {
   ret <4 x float> %1
 }
 
+define float @minimumnum_float() {
+; CHECK-LABEL: @minimumnum_float(
+; CHECK-NEXT:    ret float 5.000000e+00
+;
+  %1 = call float @llvm.minimumnum.f32(float 5.0, float 42.0)
+  ret float %1
+}
+
+define float @minimumnum_float_p0_n0() {
+; CHECK-LABEL: @minimumnum_float_p0_n0(
+; CHECK-NEXT:    ret float -0.000000e+00
+;
+  %min = call float @llvm.minimumnum.f32(float 0.0, float -0.0)
+  ret float %min
+}
+
+define float @minimumnum_float_n0_p0() {
+; CHECK-LABEL: @minimumnum_float_n0_p0(
+; CHECK-NEXT:    ret float -0.000000e+00
+;
+  %min = call float @llvm.minimumnum.f32(float -0.0, float 0.0)
+  ret float %min
+}
+
+define float @minimumnum_float_p0_qnan() {
+; CHECK-LABEL: @minimumnum_float_p0_qnan(
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+  %min = call float @llvm.minimumnum.f32(float 0.0, float 0x7FF8000000000000)
+  ret float %min
+}
+
+define float @minimumnum_float_qnan_p0() {
+; CHECK-LABEL: @minimumnum_float_qnan_p0(
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+  %min = call float @llvm.minimumnum.f32(float 0x7FF8000000000000, float 0.0)
+  ret float %min
+}
+
+define float @minimumnum_float_p0_snan() {
+; CHECK-LABEL: @minimumnum_float_p0_snan(
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+  %min = call float @llvm.minimumnum.f32(float 0.0, float 0x7FF4000000000000)
+  ret float %min
+}
+
+define float @minimumnum_float_snan_p0() {
+; CHECK-LABEL: @minimumnum_float_snan_p0(
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+  %min = call float @llvm.minimumnum.f32(float 0x7FF4000000000000, float 0.0)
+  ret float %min
+}
+
+define bfloat @minimumnum_bfloat() {
+; CHECK-LABEL: @minimumnum_bfloat(
+; CHECK-NEXT:    ret bfloat 0xR40A0
+;
+  %1 = call bfloat @llvm.minimumnum.bf16(bfloat 5.0, bfloat 42.0)
+  ret bfloat %1
+}
+
+define half @minimumnum_half() {
+; CHECK-LABEL: @minimumnum_half(
+; CHECK-NEXT:    ret half 0xH4500
+;
+  %1 = call half @llvm.minimumnum.f16(half 5.0, half 42.0)
+  ret half %1
+}
+
+; Check that minimumnum propagates its non-NaN or smaller argument
+
+define <4 x float> @minimumnum_float_vec() {
+; CHECK-LABEL: @minimumnum_float_vec(
+; CHECK-NEXT:    ret <4 x float> <float 0x7FF8000000000000, float 5.000000e+00, float 4.200000e+01, float 5.000000e+00>
+;
+  %1 = call <4 x float> @llvm.minimumnum.v4f32(<4 x float> <float 0x7FF8000000000000, float 0x7FF8000000000000, float 42., float 42.>, <4 x float> <float 0x7FF8000000000000, float 5., float 0x7FF8000000000000, float 5.>)
+  ret <4 x float> %1
+}
+
+define <4 x bfloat> @minimumnum_bfloat_vec() {
+; CHECK-LABEL: @minimumnum_bfloat_vec(
+; CHECK-NEXT:    ret <4 x bfloat> <bfloat 0xR7FC0, bfloat 0xR40A0, bfloat 0xR4228, bfloat 0xR40A0>
+;
+  %1 = call <4 x bfloat> @llvm.minimumnum.v4bf16(<4 x bfloat> <bfloat 0x7FF8000000000000, bfloat 0x7FF8000000000000, bfloat 42., bfloat 42.>, <4 x bfloat> <bfloat 0x7FF8000000000000, bfloat 5., bfloat 0x7FF8000000000000, bfloat 5.>)
+  ret <4 x bfloat> %1
+}
+
+define <4 x half> @minimumnum_half_vec() {
+; CHECK-LABEL: @minimumnum_half_vec(
+; CHECK-NEXT:    ret <4 x half> <half 0xH7E00, half 0xH4500, half 0xH5140, half 0xH4500>
+;
+  %1 = call <4 x half> @llvm.minimumnum.v4f16(<4 x half> <half 0x7FF8000000000000, half 0x7FF8000000000000, half 42., half 42.>, <4 x half> <half 0x7FF8000000000000, half 5., half 0x7FF8000000000000, half 5.>)
+  ret <4 x half> %1
+}
+
+; Check that minimumnum treats -0.0 as smaller than 0.0 while constant folding
+
+define <4 x float> @minimumnum_float_zeros_vec() {
+; CHECK-LABEL: @minimumnum_float_zeros_vec(
+; CHECK-NEXT:    ret <4 x float> <float 0.000000e+00, float -0.000000e+00, float -0.000000e+00, float -0.000000e+00>
+;
+  %1 = call <4 x float> @llvm.minimumnum.v4f32(<4 x float> <float 0.0, float -0.0, float 0.0, float -0.0>, <4 x float> <float 0.0, float 0.0, float -0.0, float -0.0>)
+  ret <4 x float> %1
+}
+
+define float @maximumnum_float() {
+; CHECK-LABEL: @maximumnum_float(
+; CHECK-NEXT:    ret float 4.200000e+01
+;
+  %1 = call float @llvm.maximumnum.f32(float 5.0, float 42.0)
+  ret float %1
+}
+
+define bfloat @maximumnum_bfloat() {
+; CHECK-LABEL: @maximumnum_bfloat(
+; CHECK-NEXT:    ret bfloat 0xR4228
+;
+  %1 = call bfloat @llvm.maximumnum.bf16(bfloat 5.0, bfloat 42.0)
+  ret bfloat %1
+}
+
+define half @maximumnum_half() {
+; CHECK-LABEL: @maximumnum_half(
+; CHECK-NEXT:    ret half 0xH5140
+;
+  %1 = call half @llvm.maximumnum.f16(half 5.0, half 42.0)
+  ret half %1
+}
+
+define float @maximumnum_float_p0_n0() {
+; CHECK-LABEL: @maximumnum_float_p0_n0(
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+  %max = call float @llvm.maximumnum.f32(float 0.0, float -0.0)
+  ret float %max
+}
+
+define float @maximumnum_float_n0_p0() {
+; CHECK-LABEL: @maximumnum_float_n0_p0(
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+  %max = call float @llvm.maximumnum.f32(float -0.0, float 0.0)
+  ret float %max
+}
+
+define float @maximumnum_float_p0_qnan() {
+; CHECK-LABEL: @maximumnum_float_p0_qnan(
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+  %max = call float @llvm.maximumnum.f32(float 0.0, float 0x7FF8000000000000)
+  ret float %max
+}
+
+define float @maximumnum_float_qnan_p0() {
+; CHECK-LABEL: @maximumnum_float_qnan_p0(
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+  %max = call float @llvm.maximumnum.f32(float 0x7FF8000000000000, float 0.0)
+  ret float %max
+}
+
+define float @maximumnum_float_p0_snan() {
+; CHECK-LABEL: @maximumnum_float_p0_snan(
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+  %max = call float @llvm.maximumnum.f32(float 0.0, float 0x7FF4000000000000)
+  ret float %max
+}
+
+define float @maximumnum_float_snan_p0() {
+; CHECK-LABEL: @maximumnum_float_snan_p0(
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+  %max = call float @llvm.maximumnum.f32(float 0x7FF4000000000000, float 0.0)
+  ret float %max
+}
+
+; Check that maximumnum propagates its non-NaN or greater argument
+
+define <4 x float> @maximumnum_float_vec() {
+; CHECK-LABEL: @maximumnum_float_vec(
+; CHECK-NEXT:    ret <4 x float> <float 0x7FF8000000000000, float 5.000000e+00, float 4.200000e+01, float 4.200000e+01>
+;
+  %1 = call <4 x float> @llvm.maximumnum.v4f32(<4 x float> <float 0x7FF8000000000000, float 0x7FF8000000000000, float 42., float 42.>, <4 x float> <float 0x7FF8000000000000, float 5., float 0x7FF8000000000000, float 5.>)
+  ret <4 x float> %1
+}
+
+define <4 x bfloat> @maximumnum_bfloat_vec() {
+; CHECK-LABEL: @maximumnum_bfloat_vec(
+; CHECK-NEXT:    ret <4 x bfloat> <bfloat 0xR7FC0, bfloat 0xR40A0, bfloat 0xR4228, bfloat 0xR4228>
+;
+  %1 = call <4 x bfloat> @llvm.maximumnum.v4bf16(<4 x bfloat> <bfloat 0x7FF8000000000000, bfloat 0x7FF8000000000000, bfloat 42., bfloat 42.>, <4 x bfloat> <bfloat 0x7FF8000000000000, bfloat 5., bfloat 0x7FF8000000000000, bfloat 5.>)
+  ret <4 x bfloat> %1
+}
+
+define <4 x half> @maximumnum_half_vec() {
+; CHECK-LABEL: @maximumnum_half_vec(
+; CHECK-NEXT:    ret <4 x half> <half 0xH7E00, half 0xH4500, half 0xH5140, half 0xH5140>
+;
+  %1 = call <4 x half> @llvm.maximumnum.v4f16(<4 x half> <half 0x7FF8000000000000, half 0x7FF8000000000000, half 42., half 42.>, <4 x half> <half 0x7FF8000000000000, half 5., half 0x7FF8000000000000, half 5.>)
+  ret <4 x half> %1
+}
+
+; Check that maximumnum treats -0.0 as smaller than 0.0 while constant folding
+
+define <4 x float> @maximumnum_float_zeros_vec() {
+; CHECK-LABEL: @maximumnum_float_zeros_vec(
+; CHECK-NEXT:    ret <4 x float> <float 0.000000e+00, float 0.000000e+00, float 0.000000e+00, float -0.000000e+00>
+;
+  %1 = call <4 x float> @llvm.maximumnum.v4f32(<4 x float> <float 0.0, float -0.0, float 0.0, float -0.0>, <4 x float> <float 0.0, float 0.0, float -0.0, float -0.0>)
+  ret <4 x float> %1
+}
+
 define i8 @smax() {
 ; CHECK-LABEL: @smax(
 ; CHECK-NEXT:    ret i8 -127

@llvmbot
Copy link
Member

llvmbot commented May 6, 2025

@llvm/pr-subscribers-llvm-analysis

Author: Lewis Crawford (LewisCrawford)

Changes

Add constant-folding support for the fmaximumnum and fminimumnum intrinsics, and extend the tests to show the qnan vs snan behavior differences between maxnum/maximum/maximumnum.


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

3 Files Affected:

  • (modified) llvm/lib/Analysis/ConstantFolding.cpp (+8)
  • (modified) llvm/test/Transforms/InstSimplify/ConstProp/fp-undef.ll (+34)
  • (modified) llvm/test/Transforms/InstSimplify/ConstProp/min-max.ll (+326)
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index 5b329e2f898f3..412a0e8979193 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -1642,6 +1642,8 @@ bool llvm::canConstantFoldCallTo(const CallBase *Call, const Function *F) {
   case Intrinsic::maxnum:
   case Intrinsic::minimum:
   case Intrinsic::maximum:
+  case Intrinsic::minimumnum:
+  case Intrinsic::maximumnum:
   case Intrinsic::log:
   case Intrinsic::log2:
   case Intrinsic::log10:
@@ -2930,6 +2932,8 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
     case Intrinsic::minnum:
     case Intrinsic::maximum:
     case Intrinsic::minimum:
+    case Intrinsic::maximumnum:
+    case Intrinsic::minimumnum:
     case Intrinsic::nvvm_fmax_d:
     case Intrinsic::nvvm_fmin_d:
       // If one argument is undef, return the other argument.
@@ -3030,6 +3034,10 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
         return ConstantFP::get(Ty->getContext(), minimum(Op1V, Op2V));
       case Intrinsic::maximum:
         return ConstantFP::get(Ty->getContext(), maximum(Op1V, Op2V));
+      case Intrinsic::minimumnum:
+        return ConstantFP::get(Ty->getContext(), minimumnum(Op1V, Op2V));
+      case Intrinsic::maximumnum:
+        return ConstantFP::get(Ty->getContext(), maximumnum(Op1V, Op2V));
 
       case Intrinsic::nvvm_fmax_d:
       case Intrinsic::nvvm_fmax_f:
diff --git a/llvm/test/Transforms/InstSimplify/ConstProp/fp-undef.ll b/llvm/test/Transforms/InstSimplify/ConstProp/fp-undef.ll
index 6fa57a8a1467a..51988583ab1ac 100644
--- a/llvm/test/Transforms/InstSimplify/ConstProp/fp-undef.ll
+++ b/llvm/test/Transforms/InstSimplify/ConstProp/fp-undef.ll
@@ -5,6 +5,8 @@ declare <2 x double> @llvm.minnum.v2f64(<2 x double>, <2 x double>)
 declare <2 x double> @llvm.maxnum.v2f64(<2 x double>, <2 x double>)
 declare <2 x double> @llvm.minimum.v2f64(<2 x double>, <2 x double>)
 declare <2 x double> @llvm.maximum.v2f64(<2 x double>, <2 x double>)
+declare <2 x double> @llvm.minimumnum.v2f64(<2 x double>, <2 x double>)
+declare <2 x double> @llvm.maximumnum.v2f64(<2 x double>, <2 x double>)
 
 ; Constant folding - undef undef.
 
@@ -538,6 +540,38 @@ define <2 x double> @frem_undef_op0_constant_vec(<2 x double> %x) {
   ret <2 x double> %r
 }
 
+define <2 x double> @maximumnum_nan_op0_vec_partial_undef_op1_undef(<2 x double> %x) {
+; CHECK-LABEL: @maximumnum_nan_op0_vec_partial_undef_op1_undef(
+; CHECK-NEXT:    ret <2 x double> <double 0x7FF8000000000000, double undef>
+;
+  %r = call <2 x double> @llvm.maximumnum.v2f64(<2 x double> <double 0x7ff8000000000000, double undef>, <2 x double> undef)
+  ret <2 x double> %r
+}
+
+define <2 x double> @maximumnum_nan_op1_vec_partial_undef_op0_undef(<2 x double> %x) {
+; CHECK-LABEL: @maximumnum_nan_op1_vec_partial_undef_op0_undef(
+; CHECK-NEXT:    ret <2 x double> <double 0x7FF8000000000000, double undef>
+;
+  %r = call <2 x double> @llvm.maximumnum.v2f64(<2 x double> undef, <2 x double> <double 0x7ff8000000000000, double undef>)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minimumnum_nan_op0_vec_partial_undef_op1_undef(<2 x double> %x) {
+; CHECK-LABEL: @minimumnum_nan_op0_vec_partial_undef_op1_undef(
+; CHECK-NEXT:    ret <2 x double> <double 0x7FF8000000000000, double undef>
+;
+  %r = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> <double 0x7ff8000000000000, double undef>, <2 x double> undef)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minimumnum_nan_op1_vec_partial_undef_op0_undef(<2 x double> %x) {
+; CHECK-LABEL: @minimumnum_nan_op1_vec_partial_undef_op0_undef(
+; CHECK-NEXT:    ret <2 x double> <double 0x7FF8000000000000, double undef>
+;
+  %r = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> undef, <2 x double> <double 0x7ff8000000000000, double undef>)
+  ret <2 x double> %r
+}
+
 define <2 x double> @maximum_nan_op0_vec_partial_undef_op1_undef(<2 x double> %x) {
 ; CHECK-LABEL: @maximum_nan_op0_vec_partial_undef_op1_undef(
 ; CHECK-NEXT:    ret <2 x double> <double 0x7FF8000000000000, double undef>
diff --git a/llvm/test/Transforms/InstSimplify/ConstProp/min-max.ll b/llvm/test/Transforms/InstSimplify/ConstProp/min-max.ll
index 9120649eb5c4f..a633d29179896 100644
--- a/llvm/test/Transforms/InstSimplify/ConstProp/min-max.ll
+++ b/llvm/test/Transforms/InstSimplify/ConstProp/min-max.ll
@@ -29,6 +29,20 @@ declare <4 x float> @llvm.maximum.v4f32(<4 x float>, <4 x float>)
 declare <4 x bfloat> @llvm.maximum.v4bf16(<4 x bfloat>, <4 x bfloat>)
 declare <4 x half> @llvm.maximum.v4f16(<4 x half>, <4 x half>)
 
+declare float @llvm.minimumnum.f32(float, float)
+declare bfloat @llvm.minimumnum.bf16(bfloat, bfloat)
+declare half @llvm.minimumnum.f16(half, half)
+declare <4 x float> @llvm.minimumnum.v4f32(<4 x float>, <4 x float>)
+declare <4 x bfloat> @llvm.minimumnum.v4bf16(<4 x bfloat>, <4 x bfloat>)
+declare <4 x half> @llvm.minimumnum.v4f16(<4 x half>, <4 x half>)
+
+declare float @llvm.maximumnum.f32(float, float)
+declare bfloat @llvm.maximumnum.bf16(bfloat, bfloat)
+declare half @llvm.maximumnum.f16(half, half)
+declare <4 x float> @llvm.maximumnum.v4f32(<4 x float>, <4 x float>)
+declare <4 x bfloat> @llvm.maximumnum.v4bf16(<4 x bfloat>, <4 x bfloat>)
+declare <4 x half> @llvm.maximumnum.v4f16(<4 x half>, <4 x half>)
+
 declare i8 @llvm.smax.i8(i8, i8)
 declare <5 x i8> @llvm.smax.v5i8(<5 x i8>, <5 x i8>)
 
@@ -81,6 +95,22 @@ define float @minnum_float_qnan_p0() {
   ret float %min
 }
 
+define float @minnum_float_p0_snan() {
+; CHECK-LABEL: @minnum_float_p0_snan(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %min = call float @llvm.minnum.f32(float 0.0, float 0x7FF4000000000000)
+  ret float %min
+}
+
+define float @minnum_float_snan_p0() {
+; CHECK-LABEL: @minnum_float_snan_p0(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %min = call float @llvm.minnum.f32(float 0x7FF4000000000000, float 0.0)
+  ret float %min
+}
+
 define bfloat @minnum_bfloat() {
 ; CHECK-LABEL: @minnum_bfloat(
 ; CHECK-NEXT:    ret bfloat 0xR40A0
@@ -173,6 +203,22 @@ define float @maxnum_float_qnan_p0() {
   ret float %max
 }
 
+define float @maxnum_float_p0_snan() {
+; CHECK-LABEL: @maxnum_float_p0_snan(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %max = call float @llvm.maxnum.f32(float 0.0, float 0x7FF4000000000000)
+  ret float %max
+}
+
+define float @maxnum_float_snan_p0() {
+; CHECK-LABEL: @maxnum_float_snan_p0(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %max = call float @llvm.maxnum.f32(float 0x7FF4000000000000, float 0.0)
+  ret float %max
+}
+
 define bfloat @maxnum_bfloat() {
 ; CHECK-LABEL: @maxnum_bfloat(
 ; CHECK-NEXT:    ret bfloat 0xR4228
@@ -249,6 +295,38 @@ define half @minimum_half() {
   ret half %1
 }
 
+define float @minimum_float_p0_qnan() {
+; CHECK-LABEL: @minimum_float_p0_qnan(
+; CHECK-NEXT:    ret float 0x7FF8000000000000
+;
+  %max = call float @llvm.minimum.f32(float 0.0, float 0x7FF8000000000000)
+  ret float %max
+}
+
+define float @minimum_float_qnan_p0() {
+; CHECK-LABEL: @minimum_float_qnan_p0(
+; CHECK-NEXT:    ret float 0x7FF8000000000000
+;
+  %max = call float @llvm.minimum.f32(float 0x7FF8000000000000, float 0.0)
+  ret float %max
+}
+
+define float @minimum_float_p0_snan() {
+; CHECK-LABEL: @minimum_float_p0_snan(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %max = call float @llvm.minimum.f32(float 0.0, float 0x7FF4000000000000)
+  ret float %max
+}
+
+define float @minimum_float_snan_p0() {
+; CHECK-LABEL: @minimum_float_snan_p0(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %max = call float @llvm.minimum.f32(float 0x7FF4000000000000, float 0.0)
+  ret float %max
+}
+
 ; Check that minimum propagates its NaN or smaller argument
 
 define <4 x float> @minimum_float_vec() {
@@ -309,6 +387,38 @@ define half @maximum_half() {
   ret half %1
 }
 
+define float @maximum_float_p0_qnan() {
+; CHECK-LABEL: @maximum_float_p0_qnan(
+; CHECK-NEXT:    ret float 0x7FF8000000000000
+;
+  %max = call float @llvm.maximum.f32(float 0.0, float 0x7FF8000000000000)
+  ret float %max
+}
+
+define float @maximum_float_qnan_p0() {
+; CHECK-LABEL: @maximum_float_qnan_p0(
+; CHECK-NEXT:    ret float 0x7FF8000000000000
+;
+  %max = call float @llvm.maximum.f32(float 0x7FF8000000000000, float 0.0)
+  ret float %max
+}
+
+define float @maximum_float_p0_snan() {
+; CHECK-LABEL: @maximum_float_p0_snan(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %max = call float @llvm.maximum.f32(float 0.0, float 0x7FF4000000000000)
+  ret float %max
+}
+
+define float @maximum_float_snan_p0() {
+; CHECK-LABEL: @maximum_float_snan_p0(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %max = call float @llvm.maximum.f32(float 0x7FF4000000000000, float 0.0)
+  ret float %max
+}
+
 ; Check that maximum propagates its NaN or greater argument
 
 define <4 x float> @maximum_float_vec() {
@@ -345,6 +455,222 @@ define <4 x float> @maximum_float_zeros_vec() {
   ret <4 x float> %1
 }
 
+define float @minimumnum_float() {
+; CHECK-LABEL: @minimumnum_float(
+; CHECK-NEXT:    ret float 5.000000e+00
+;
+  %1 = call float @llvm.minimumnum.f32(float 5.0, float 42.0)
+  ret float %1
+}
+
+define float @minimumnum_float_p0_n0() {
+; CHECK-LABEL: @minimumnum_float_p0_n0(
+; CHECK-NEXT:    ret float -0.000000e+00
+;
+  %min = call float @llvm.minimumnum.f32(float 0.0, float -0.0)
+  ret float %min
+}
+
+define float @minimumnum_float_n0_p0() {
+; CHECK-LABEL: @minimumnum_float_n0_p0(
+; CHECK-NEXT:    ret float -0.000000e+00
+;
+  %min = call float @llvm.minimumnum.f32(float -0.0, float 0.0)
+  ret float %min
+}
+
+define float @minimumnum_float_p0_qnan() {
+; CHECK-LABEL: @minimumnum_float_p0_qnan(
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+  %min = call float @llvm.minimumnum.f32(float 0.0, float 0x7FF8000000000000)
+  ret float %min
+}
+
+define float @minimumnum_float_qnan_p0() {
+; CHECK-LABEL: @minimumnum_float_qnan_p0(
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+  %min = call float @llvm.minimumnum.f32(float 0x7FF8000000000000, float 0.0)
+  ret float %min
+}
+
+define float @minimumnum_float_p0_snan() {
+; CHECK-LABEL: @minimumnum_float_p0_snan(
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+  %min = call float @llvm.minimumnum.f32(float 0.0, float 0x7FF4000000000000)
+  ret float %min
+}
+
+define float @minimumnum_float_snan_p0() {
+; CHECK-LABEL: @minimumnum_float_snan_p0(
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+  %min = call float @llvm.minimumnum.f32(float 0x7FF4000000000000, float 0.0)
+  ret float %min
+}
+
+define bfloat @minimumnum_bfloat() {
+; CHECK-LABEL: @minimumnum_bfloat(
+; CHECK-NEXT:    ret bfloat 0xR40A0
+;
+  %1 = call bfloat @llvm.minimumnum.bf16(bfloat 5.0, bfloat 42.0)
+  ret bfloat %1
+}
+
+define half @minimumnum_half() {
+; CHECK-LABEL: @minimumnum_half(
+; CHECK-NEXT:    ret half 0xH4500
+;
+  %1 = call half @llvm.minimumnum.f16(half 5.0, half 42.0)
+  ret half %1
+}
+
+; Check that minimumnum propagates its non-NaN or smaller argument
+
+define <4 x float> @minimumnum_float_vec() {
+; CHECK-LABEL: @minimumnum_float_vec(
+; CHECK-NEXT:    ret <4 x float> <float 0x7FF8000000000000, float 5.000000e+00, float 4.200000e+01, float 5.000000e+00>
+;
+  %1 = call <4 x float> @llvm.minimumnum.v4f32(<4 x float> <float 0x7FF8000000000000, float 0x7FF8000000000000, float 42., float 42.>, <4 x float> <float 0x7FF8000000000000, float 5., float 0x7FF8000000000000, float 5.>)
+  ret <4 x float> %1
+}
+
+define <4 x bfloat> @minimumnum_bfloat_vec() {
+; CHECK-LABEL: @minimumnum_bfloat_vec(
+; CHECK-NEXT:    ret <4 x bfloat> <bfloat 0xR7FC0, bfloat 0xR40A0, bfloat 0xR4228, bfloat 0xR40A0>
+;
+  %1 = call <4 x bfloat> @llvm.minimumnum.v4bf16(<4 x bfloat> <bfloat 0x7FF8000000000000, bfloat 0x7FF8000000000000, bfloat 42., bfloat 42.>, <4 x bfloat> <bfloat 0x7FF8000000000000, bfloat 5., bfloat 0x7FF8000000000000, bfloat 5.>)
+  ret <4 x bfloat> %1
+}
+
+define <4 x half> @minimumnum_half_vec() {
+; CHECK-LABEL: @minimumnum_half_vec(
+; CHECK-NEXT:    ret <4 x half> <half 0xH7E00, half 0xH4500, half 0xH5140, half 0xH4500>
+;
+  %1 = call <4 x half> @llvm.minimumnum.v4f16(<4 x half> <half 0x7FF8000000000000, half 0x7FF8000000000000, half 42., half 42.>, <4 x half> <half 0x7FF8000000000000, half 5., half 0x7FF8000000000000, half 5.>)
+  ret <4 x half> %1
+}
+
+; Check that minimumnum treats -0.0 as smaller than 0.0 while constant folding
+
+define <4 x float> @minimumnum_float_zeros_vec() {
+; CHECK-LABEL: @minimumnum_float_zeros_vec(
+; CHECK-NEXT:    ret <4 x float> <float 0.000000e+00, float -0.000000e+00, float -0.000000e+00, float -0.000000e+00>
+;
+  %1 = call <4 x float> @llvm.minimumnum.v4f32(<4 x float> <float 0.0, float -0.0, float 0.0, float -0.0>, <4 x float> <float 0.0, float 0.0, float -0.0, float -0.0>)
+  ret <4 x float> %1
+}
+
+define float @maximumnum_float() {
+; CHECK-LABEL: @maximumnum_float(
+; CHECK-NEXT:    ret float 4.200000e+01
+;
+  %1 = call float @llvm.maximumnum.f32(float 5.0, float 42.0)
+  ret float %1
+}
+
+define bfloat @maximumnum_bfloat() {
+; CHECK-LABEL: @maximumnum_bfloat(
+; CHECK-NEXT:    ret bfloat 0xR4228
+;
+  %1 = call bfloat @llvm.maximumnum.bf16(bfloat 5.0, bfloat 42.0)
+  ret bfloat %1
+}
+
+define half @maximumnum_half() {
+; CHECK-LABEL: @maximumnum_half(
+; CHECK-NEXT:    ret half 0xH5140
+;
+  %1 = call half @llvm.maximumnum.f16(half 5.0, half 42.0)
+  ret half %1
+}
+
+define float @maximumnum_float_p0_n0() {
+; CHECK-LABEL: @maximumnum_float_p0_n0(
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+  %max = call float @llvm.maximumnum.f32(float 0.0, float -0.0)
+  ret float %max
+}
+
+define float @maximumnum_float_n0_p0() {
+; CHECK-LABEL: @maximumnum_float_n0_p0(
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+  %max = call float @llvm.maximumnum.f32(float -0.0, float 0.0)
+  ret float %max
+}
+
+define float @maximumnum_float_p0_qnan() {
+; CHECK-LABEL: @maximumnum_float_p0_qnan(
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+  %max = call float @llvm.maximumnum.f32(float 0.0, float 0x7FF8000000000000)
+  ret float %max
+}
+
+define float @maximumnum_float_qnan_p0() {
+; CHECK-LABEL: @maximumnum_float_qnan_p0(
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+  %max = call float @llvm.maximumnum.f32(float 0x7FF8000000000000, float 0.0)
+  ret float %max
+}
+
+define float @maximumnum_float_p0_snan() {
+; CHECK-LABEL: @maximumnum_float_p0_snan(
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+  %max = call float @llvm.maximumnum.f32(float 0.0, float 0x7FF4000000000000)
+  ret float %max
+}
+
+define float @maximumnum_float_snan_p0() {
+; CHECK-LABEL: @maximumnum_float_snan_p0(
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+  %max = call float @llvm.maximumnum.f32(float 0x7FF4000000000000, float 0.0)
+  ret float %max
+}
+
+; Check that maximumnum propagates its non-NaN or greater argument
+
+define <4 x float> @maximumnum_float_vec() {
+; CHECK-LABEL: @maximumnum_float_vec(
+; CHECK-NEXT:    ret <4 x float> <float 0x7FF8000000000000, float 5.000000e+00, float 4.200000e+01, float 4.200000e+01>
+;
+  %1 = call <4 x float> @llvm.maximumnum.v4f32(<4 x float> <float 0x7FF8000000000000, float 0x7FF8000000000000, float 42., float 42.>, <4 x float> <float 0x7FF8000000000000, float 5., float 0x7FF8000000000000, float 5.>)
+  ret <4 x float> %1
+}
+
+define <4 x bfloat> @maximumnum_bfloat_vec() {
+; CHECK-LABEL: @maximumnum_bfloat_vec(
+; CHECK-NEXT:    ret <4 x bfloat> <bfloat 0xR7FC0, bfloat 0xR40A0, bfloat 0xR4228, bfloat 0xR4228>
+;
+  %1 = call <4 x bfloat> @llvm.maximumnum.v4bf16(<4 x bfloat> <bfloat 0x7FF8000000000000, bfloat 0x7FF8000000000000, bfloat 42., bfloat 42.>, <4 x bfloat> <bfloat 0x7FF8000000000000, bfloat 5., bfloat 0x7FF8000000000000, bfloat 5.>)
+  ret <4 x bfloat> %1
+}
+
+define <4 x half> @maximumnum_half_vec() {
+; CHECK-LABEL: @maximumnum_half_vec(
+; CHECK-NEXT:    ret <4 x half> <half 0xH7E00, half 0xH4500, half 0xH5140, half 0xH5140>
+;
+  %1 = call <4 x half> @llvm.maximumnum.v4f16(<4 x half> <half 0x7FF8000000000000, half 0x7FF8000000000000, half 42., half 42.>, <4 x half> <half 0x7FF8000000000000, half 5., half 0x7FF8000000000000, half 5.>)
+  ret <4 x half> %1
+}
+
+; Check that maximumnum treats -0.0 as smaller than 0.0 while constant folding
+
+define <4 x float> @maximumnum_float_zeros_vec() {
+; CHECK-LABEL: @maximumnum_float_zeros_vec(
+; CHECK-NEXT:    ret <4 x float> <float 0.000000e+00, float 0.000000e+00, float 0.000000e+00, float -0.000000e+00>
+;
+  %1 = call <4 x float> @llvm.maximumnum.v4f32(<4 x float> <float 0.0, float -0.0, float 0.0, float -0.0>, <4 x float> <float 0.0, float 0.0, float -0.0, float -0.0>)
+  ret <4 x float> %1
+}
+
 define i8 @smax() {
 ; CHECK-LABEL: @smax(
 ; CHECK-NEXT:    ret i8 -127

Copy link

github-actions bot commented May 6, 2025

✅ With the latest revision this PR passed the undef deprecator.

Add constant-folding support for the maximumnum and minimumnum
intrinsics, and extend the tests to show the qnan vs snan behavior
differences between maxnum/maximum/maximumnum.
@LewisCrawford LewisCrawford force-pushed the fold_maximumnum_minimumnum branch from 7b2bddf to 4c9bf0f Compare May 6, 2025 15:05
@LewisCrawford LewisCrawford changed the title [ConstantFolding] Fold fmaximumnum and fminimumnum [ConstantFolding] Fold maximumnum and minimumnum May 6, 2025
@dtcxzyw dtcxzyw requested a review from arsenm May 6, 2025 15:39
@arsenm arsenm merged commit 9c88b6d into llvm:main May 8, 2025
9 of 10 checks passed
@arsenm
Copy link
Contributor

arsenm commented May 9, 2025

Are you working on the generalized InstSimplify handling for these beyond constant folding?

@LewisCrawford
Copy link
Contributor Author

LewisCrawford commented May 9, 2025

Yes, I am working on a patch to add more optimization support for maximumnum/minimumnum in InstSimplify and ValueTracking.

(Patch here: #139581 )

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

Successfully merging this pull request may close these issues.

3 participants