Index: lib/Analysis/InstructionSimplify.cpp =================================================================== --- lib/Analysis/InstructionSimplify.cpp +++ lib/Analysis/InstructionSimplify.cpp @@ -4829,13 +4829,24 @@ } break; case Intrinsic::maxnum: - case Intrinsic::minnum: { + case Intrinsic::minnum: + case Intrinsic::maxnan: + case Intrinsic::minnan: { // If the arguments are the same, this is a no-op. if (Op0 == Op1) return Op0; - // If one argument is NaN or undef, return the other argument. - if (match(Op0, m_CombineOr(m_NaN(), m_Undef()))) return Op1; - if (match(Op1, m_CombineOr(m_NaN(), m_Undef()))) return Op0; + // If one argument is undef, return the other argument. + if (match(Op0, m_Undef())) + return Op1; + if (match(Op1, m_Undef())) + return Op0; + + // If one argument is NaN, return other or NaN appropriately. + bool PropagateNaN = (IID == Intrinsic::minnan || IID == Intrinsic::maxnan); + if (match(Op0, m_NaN())) + return PropagateNaN ? Op0 : Op1; + if (match(Op1, m_NaN())) + return PropagateNaN ? Op1 : Op0; // Min/max of the same operation with common operand: // m(m(X, Y)), X --> m(X, Y) (4 commuted variants) @@ -4848,9 +4859,9 @@ (M1->getOperand(0) == Op0 || M1->getOperand(1) == Op0)) return Op1; - // minnum(X, -Inf) --> -Inf (and commuted variant) - // maxnum(X, +Inf) --> +Inf (and commuted variant) - bool UseNegInf = IID == Intrinsic::minnum; + // min(X, -Inf) --> -Inf (and commuted variant) + // max(X, +Inf) --> +Inf (and commuted variant) + bool UseNegInf = (IID == Intrinsic::minnum || IID == Intrinsic::minnan); const APFloat *C; if ((match(Op0, m_APFloat(C)) && C->isInfinity() && C->isNegative() == UseNegInf) || Index: lib/Analysis/ValueTracking.cpp =================================================================== --- lib/Analysis/ValueTracking.cpp +++ lib/Analysis/ValueTracking.cpp @@ -2898,7 +2898,13 @@ cannotBeOrderedLessThanZeroImpl(I->getOperand(1), TLI, SignBitOnly, Depth + 1)); + case Intrinsic::maxnan: + return cannotBeOrderedLessThanZeroImpl(I->getOperand(0), TLI, SignBitOnly, + Depth + 1) || + cannotBeOrderedLessThanZeroImpl(I->getOperand(0), TLI, SignBitOnly, + Depth + 1); case Intrinsic::minnum: + case Intrinsic::minnan: return cannotBeOrderedLessThanZeroImpl(I->getOperand(0), TLI, SignBitOnly, Depth + 1) && cannotBeOrderedLessThanZeroImpl(I->getOperand(1), TLI, SignBitOnly, Index: lib/Transforms/InstCombine/InstCombineCalls.cpp =================================================================== --- lib/Transforms/InstCombine/InstCombineCalls.cpp +++ lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -2020,7 +2020,9 @@ } case Intrinsic::minnum: - case Intrinsic::maxnum: { + case Intrinsic::maxnum: + case Intrinsic::minnan: + case Intrinsic::maxnan: { Value *Arg0 = II->getArgOperand(0); Value *Arg1 = II->getArgOperand(1); // Canonicalize constants to the RHS. @@ -2034,10 +2036,25 @@ if (match(Arg0, m_FNeg(m_Value(X))) && match(Arg1, m_FNeg(m_Value(Y))) && (Arg0->hasOneUse() || Arg1->hasOneUse())) { // If both operands are negated, invert the call and negate the result: - // minnum(-X, -Y) --> -(maxnum(X, Y)) - // maxnum(-X, -Y) --> -(minnum(X, Y)) - Intrinsic::ID NewIID = II->getIntrinsicID() == Intrinsic::maxnum ? - Intrinsic::minnum : Intrinsic::maxnum; + // min(-X, -Y) --> -(max(X, Y)) + // max(-X, -Y) --> -(min(X, Y)) + Intrinsic::ID NewIID; + switch (II->getIntrinsicID()) { + case Intrinsic::maxnum: + NewIID = Intrinsic::minnum; + break; + case Intrinsic::minnum: + NewIID = Intrinsic::maxnum; + break; + case Intrinsic::maxnan: + NewIID = Intrinsic::minnan; + break; + case Intrinsic::minnan: + NewIID = Intrinsic::maxnan; + break; + default: + llvm_unreachable("unexpected intrinsic ID"); + } Value *NewCall = Builder.CreateIntrinsic(NewIID, { X, Y }, II); Instruction *FNeg = BinaryOperator::CreateFNeg(NewCall); FNeg->copyIRFlags(II); Index: test/Transforms/InstCombine/maxnan.ll =================================================================== --- /dev/null +++ test/Transforms/InstCombine/maxnan.ll @@ -0,0 +1,238 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -instcombine < %s | FileCheck %s + +declare float @llvm.maxnan.f32(float, float) +declare <2 x float> @llvm.maxnan.v2f32(<2 x float>, <2 x float>) +declare <4 x float> @llvm.maxnan.v4f32(<4 x float>, <4 x float>) + +declare double @llvm.maxnan.f64(double, double) +declare <2 x double> @llvm.maxnan.v2f64(<2 x double>, <2 x double>) + +define float @constant_fold_maxnan_f32() { +; CHECK-LABEL: @constant_fold_maxnan_f32( +; CHECK-NEXT: ret float 2.000000e+00 +; + %x = call float @llvm.maxnan.f32(float 1.0, float 2.0) + ret float %x +} + +define float @constant_fold_maxnan_f32_inv() { +; CHECK-LABEL: @constant_fold_maxnan_f32_inv( +; CHECK-NEXT: ret float 2.000000e+00 +; + %x = call float @llvm.maxnan.f32(float 2.0, float 1.0) + ret float %x +} + +define float @constant_fold_maxnan_f32_nan0() { +; CHECK-LABEL: @constant_fold_maxnan_f32_nan0( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %x = call float @llvm.maxnan.f32(float 0x7FF8000000000000, float 2.0) + ret float %x +} + +define float @constant_fold_maxnan_f32_nan1() { +; CHECK-LABEL: @constant_fold_maxnan_f32_nan1( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %x = call float @llvm.maxnan.f32(float 2.0, float 0x7FF8000000000000) + ret float %x +} + +define float @constant_fold_maxnan_f32_nan_nan() { +; CHECK-LABEL: @constant_fold_maxnan_f32_nan_nan( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %x = call float @llvm.maxnan.f32(float 0x7FF8000000000000, float 0x7FF8000000000000) + ret float %x +} + +define float @constant_fold_maxnan_f32_p0_p0() { +; CHECK-LABEL: @constant_fold_maxnan_f32_p0_p0( +; CHECK-NEXT: ret float 0.000000e+00 +; + %x = call float @llvm.maxnan.f32(float 0.0, float 0.0) + ret float %x +} + +define float @constant_fold_maxnan_f32_p0_n0() { +; CHECK-LABEL: @constant_fold_maxnan_f32_p0_n0( +; CHECK-NEXT: ret float 0.000000e+00 +; + %x = call float @llvm.maxnan.f32(float 0.0, float -0.0) + ret float %x +} + +define float @constant_fold_maxnan_f32_n0_p0() { +; CHECK-LABEL: @constant_fold_maxnan_f32_n0_p0( +; CHECK-NEXT: ret float -0.000000e+00 +; + %x = call float @llvm.maxnan.f32(float -0.0, float 0.0) + ret float %x +} + +define float @constant_fold_maxnan_f32_n0_n0() { +; CHECK-LABEL: @constant_fold_maxnan_f32_n0_n0( +; CHECK-NEXT: ret float -0.000000e+00 +; + %x = call float @llvm.maxnan.f32(float -0.0, float -0.0) + ret float %x +} + +define <4 x float> @constant_fold_maxnan_v4f32() { +; CHECK-LABEL: @constant_fold_maxnan_v4f32( +; CHECK-NEXT: ret <4 x float> +; + %x = call <4 x float> @llvm.maxnan.v4f32(<4 x float> , <4 x float> ) + ret <4 x float> %x +} + +define double @constant_fold_maxnan_f64() { +; CHECK-LABEL: @constant_fold_maxnan_f64( +; CHECK-NEXT: ret double 2.000000e+00 +; + %x = call double @llvm.maxnan.f64(double 1.0, double 2.0) + ret double %x +} + +define double @constant_fold_maxnan_f64_nan0() { +; CHECK-LABEL: @constant_fold_maxnan_f64_nan0( +; CHECK-NEXT: ret double 0x7FF8000000000000 +; + %x = call double @llvm.maxnan.f64(double 0x7FF8000000000000, double 2.0) + ret double %x +} + +define double @constant_fold_maxnan_f64_nan1() { +; CHECK-LABEL: @constant_fold_maxnan_f64_nan1( +; CHECK-NEXT: ret double 0x7FF8000000000000 +; + %x = call double @llvm.maxnan.f64(double 2.0, double 0x7FF8000000000000) + ret double %x +} + +define double @constant_fold_maxnan_f64_nan_nan() { +; CHECK-LABEL: @constant_fold_maxnan_f64_nan_nan( +; CHECK-NEXT: ret double 0x7FF8000000000000 +; + %x = call double @llvm.maxnan.f64(double 0x7FF8000000000000, double 0x7FF8000000000000) + ret double %x +} + +define float @canonicalize_constant_maxnan_f32(float %x) { +; CHECK-LABEL: @canonicalize_constant_maxnan_f32( +; CHECK-NEXT: [[Y:%.*]] = call float @llvm.maxnan.f32(float [[X:%.*]], float 1.000000e+00) +; CHECK-NEXT: ret float [[Y]] +; + %y = call float @llvm.maxnan.f32(float 1.0, float %x) + ret float %y +} + +define float @maxnan_f32_nan_val(float %x) { +; CHECK-LABEL: @maxnan_f32_nan_val( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %y = call float @llvm.maxnan.f32(float 0x7FF8000000000000, float %x) + ret float %y +} + +define float @maxnan_f32_val_nan(float %x) { +; CHECK-LABEL: @maxnan_f32_val_nan( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %y = call float @llvm.maxnan.f32(float %x, float 0x7FF8000000000000) + ret float %y +} + +define float @maxnan4(float %x, float %y, float %z, float %w) { +; CHECK-LABEL: @maxnan4( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.maxnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: [[B:%.*]] = call float @llvm.maxnan.f32(float [[Z:%.*]], float [[W:%.*]]) +; CHECK-NEXT: [[C:%.*]] = call float @llvm.maxnan.f32(float [[A]], float [[B]]) +; CHECK-NEXT: ret float [[C]] +; + %a = call float @llvm.maxnan.f32(float %x, float %y) + %b = call float @llvm.maxnan.f32(float %z, float %w) + %c = call float @llvm.maxnan.f32(float %a, float %b) + ret float %c +} + +; PR37404 - https://bugs.llvm.org/show_bug.cgi?id=37404 + +define <2 x float> @neg_neg(<2 x float> %x, <2 x float> %y) { +; CHECK-LABEL: @neg_neg( +; CHECK-NEXT: [[TMP1:%.*]] = call <2 x float> @llvm.minnan.v2f32(<2 x float> [[X:%.*]], <2 x float> [[Y:%.*]]) +; CHECK-NEXT: [[R:%.*]] = fsub <2 x float> , [[TMP1]] +; CHECK-NEXT: ret <2 x float> [[R]] +; + %negx = fsub <2 x float> , %x + %negy = fsub <2 x float> , %y + %r = call <2 x float> @llvm.maxnan.v2f32(<2 x float> %negx, <2 x float> %negy) + ret <2 x float> %r +} + +; FMF is not required, but it should be propagated from the intrinsic (not the fnegs). + +define float @neg_neg_vec_fmf(float %x, float %y) { +; CHECK-LABEL: @neg_neg_vec_fmf( +; CHECK-NEXT: [[TMP1:%.*]] = call fast float @llvm.minnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: [[R:%.*]] = fsub fast float -0.000000e+00, [[TMP1]] +; CHECK-NEXT: ret float [[R]] +; + %negx = fsub arcp float -0.0, %x + %negy = fsub afn float -0.0, %y + %r = call fast float @llvm.maxnan.f32(float %negx, float %negy) + ret float %r +} + +; 1 extra use of an intermediate value should still allow the fold, +; but 2 would require more instructions than we started with. + +declare void @use(float) +define float @neg_neg_extra_use_x(float %x, float %y) { +; CHECK-LABEL: @neg_neg_extra_use_x( +; CHECK-NEXT: [[NEGX:%.*]] = fsub float -0.000000e+00, [[X:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.minnan.f32(float [[X]], float [[Y:%.*]]) +; CHECK-NEXT: [[R:%.*]] = fsub float -0.000000e+00, [[TMP1]] +; CHECK-NEXT: call void @use(float [[NEGX]]) +; CHECK-NEXT: ret float [[R]] +; + %negx = fsub float -0.0, %x + %negy = fsub float -0.0, %y + %r = call float @llvm.maxnan.f32(float %negx, float %negy) + call void @use(float %negx) + ret float %r +} + +define float @neg_neg_extra_use_y(float %x, float %y) { +; CHECK-LABEL: @neg_neg_extra_use_y( +; CHECK-NEXT: [[NEGY:%.*]] = fsub float -0.000000e+00, [[Y:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.minnan.f32(float [[X:%.*]], float [[Y]]) +; CHECK-NEXT: [[R:%.*]] = fsub float -0.000000e+00, [[TMP1]] +; CHECK-NEXT: call void @use(float [[NEGY]]) +; CHECK-NEXT: ret float [[R]] +; + %negx = fsub float -0.0, %x + %negy = fsub float -0.0, %y + %r = call float @llvm.maxnan.f32(float %negx, float %negy) + call void @use(float %negy) + ret float %r +} + +define float @neg_neg_extra_use_x_and_y(float %x, float %y) { +; CHECK-LABEL: @neg_neg_extra_use_x_and_y( +; CHECK-NEXT: [[NEGX:%.*]] = fsub float -0.000000e+00, [[X:%.*]] +; CHECK-NEXT: [[NEGY:%.*]] = fsub float -0.000000e+00, [[Y:%.*]] +; CHECK-NEXT: [[R:%.*]] = call float @llvm.maxnan.f32(float [[NEGX]], float [[NEGY]]) +; CHECK-NEXT: call void @use(float [[NEGX]]) +; CHECK-NEXT: call void @use(float [[NEGY]]) +; CHECK-NEXT: ret float [[R]] +; + %negx = fsub float -0.0, %x + %negy = fsub float -0.0, %y + %r = call float @llvm.maxnan.f32(float %negx, float %negy) + call void @use(float %negx) + call void @use(float %negy) + ret float %r +} Index: test/Transforms/InstCombine/minnan.ll =================================================================== --- /dev/null +++ test/Transforms/InstCombine/minnan.ll @@ -0,0 +1,263 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -instcombine < %s | FileCheck %s + +declare float @llvm.minnan.f32(float, float) +declare float @llvm.minnan.v2f32(<2 x float>, <2 x float>) +declare <4 x float> @llvm.minnan.v4f32(<4 x float>, <4 x float>) + +declare double @llvm.minnan.f64(double, double) +declare <2 x double> @llvm.minnan.v2f64(<2 x double>, <2 x double>) + +declare float @llvm.maxnan.f32(float, float) + +define float @constant_fold_minnan_f32() { +; CHECK-LABEL: @constant_fold_minnan_f32( +; CHECK-NEXT: ret float 1.000000e+00 +; + %x = call float @llvm.minnan.f32(float 1.0, float 2.0) + ret float %x +} + +define float @constant_fold_minnan_f32_inv() { +; CHECK-LABEL: @constant_fold_minnan_f32_inv( +; CHECK-NEXT: ret float 1.000000e+00 +; + %x = call float @llvm.minnan.f32(float 2.0, float 1.0) + ret float %x +} + +define float @constant_fold_minnan_f32_nan0() { +; CHECK-LABEL: @constant_fold_minnan_f32_nan0( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %x = call float @llvm.minnan.f32(float 0x7FF8000000000000, float 2.0) + ret float %x +} + +define float @constant_fold_minnan_f32_nan1() { +; CHECK-LABEL: @constant_fold_minnan_f32_nan1( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %x = call float @llvm.minnan.f32(float 2.0, float 0x7FF8000000000000) + ret float %x +} + +define float @constant_fold_minnan_f32_nan_nan() { +; CHECK-LABEL: @constant_fold_minnan_f32_nan_nan( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %x = call float @llvm.minnan.f32(float 0x7FF8000000000000, float 0x7FF8000000000000) + ret float %x +} + +define float @constant_fold_minnan_f32_p0_p0() { +; CHECK-LABEL: @constant_fold_minnan_f32_p0_p0( +; CHECK-NEXT: ret float 0.000000e+00 +; + %x = call float @llvm.minnan.f32(float 0.0, float 0.0) + ret float %x +} + +define float @constant_fold_minnan_f32_p0_n0() { +; CHECK-LABEL: @constant_fold_minnan_f32_p0_n0( +; CHECK-NEXT: ret float 0.000000e+00 +; + %x = call float @llvm.minnan.f32(float 0.0, float -0.0) + ret float %x +} + +define float @constant_fold_minnan_f32_n0_p0() { +; CHECK-LABEL: @constant_fold_minnan_f32_n0_p0( +; CHECK-NEXT: ret float -0.000000e+00 +; + %x = call float @llvm.minnan.f32(float -0.0, float 0.0) + ret float %x +} + +define float @constant_fold_minnan_f32_n0_n0() { +; CHECK-LABEL: @constant_fold_minnan_f32_n0_n0( +; CHECK-NEXT: ret float -0.000000e+00 +; + %x = call float @llvm.minnan.f32(float -0.0, float -0.0) + ret float %x +} + +define <4 x float> @constant_fold_minnan_v4f32() { +; CHECK-LABEL: @constant_fold_minnan_v4f32( +; CHECK-NEXT: ret <4 x float> +; + %x = call <4 x float> @llvm.minnan.v4f32(<4 x float> , <4 x float> ) + ret <4 x float> %x +} + +define double @constant_fold_minnan_f64() { +; CHECK-LABEL: @constant_fold_minnan_f64( +; CHECK-NEXT: ret double 1.000000e+00 +; + %x = call double @llvm.minnan.f64(double 1.0, double 2.0) + ret double %x +} + +define double @constant_fold_minnan_f64_nan0() { +; CHECK-LABEL: @constant_fold_minnan_f64_nan0( +; CHECK-NEXT: ret double 0x7FF8000000000000 +; + %x = call double @llvm.minnan.f64(double 0x7FF8000000000000, double 2.0) + ret double %x +} + +define double @constant_fold_minnan_f64_nan1() { +; CHECK-LABEL: @constant_fold_minnan_f64_nan1( +; CHECK-NEXT: ret double 0x7FF8000000000000 +; + %x = call double @llvm.minnan.f64(double 2.0, double 0x7FF8000000000000) + ret double %x +} + +define double @constant_fold_minnan_f64_nan_nan() { +; CHECK-LABEL: @constant_fold_minnan_f64_nan_nan( +; CHECK-NEXT: ret double 0x7FF8000000000000 +; + %x = call double @llvm.minnan.f64(double 0x7FF8000000000000, double 0x7FF8000000000000) + ret double %x +} + +define float @canonicalize_constant_minnan_f32(float %x) { +; CHECK-LABEL: @canonicalize_constant_minnan_f32( +; CHECK-NEXT: [[Y:%.*]] = call float @llvm.minnan.f32(float [[X:%.*]], float 1.000000e+00) +; CHECK-NEXT: ret float [[Y]] +; + %y = call float @llvm.minnan.f32(float 1.0, float %x) + ret float %y +} + +define float @minnan_f32_nan_val(float %x) { +; CHECK-LABEL: @minnan_f32_nan_val( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %y = call float @llvm.minnan.f32(float 0x7FF8000000000000, float %x) + ret float %y +} + +define float @minnan_f32_val_nan(float %x) { +; CHECK-LABEL: @minnan_f32_val_nan( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %y = call float @llvm.minnan.f32(float %x, float 0x7FF8000000000000) + ret float %y +} + +define float @minnan4(float %x, float %y, float %z, float %w) { +; CHECK-LABEL: @minnan4( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.minnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: [[B:%.*]] = call float @llvm.minnan.f32(float [[Z:%.*]], float [[W:%.*]]) +; CHECK-NEXT: [[C:%.*]] = call float @llvm.minnan.f32(float [[A]], float [[B]]) +; CHECK-NEXT: ret float [[C]] +; + %a = call float @llvm.minnan.f32(float %x, float %y) + %b = call float @llvm.minnan.f32(float %z, float %w) + %c = call float @llvm.minnan.f32(float %a, float %b) + ret float %c +} + +define float @minnan_x_maxnan_x_y(float %x, float %y) { +; CHECK-LABEL: @minnan_x_maxnan_x_y( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.maxnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: [[B:%.*]] = call float @llvm.minnan.f32(float [[X]], float [[A]]) +; CHECK-NEXT: ret float [[B]] +; + %a = call float @llvm.maxnan.f32(float %x, float %y) + %b = call float @llvm.minnan.f32(float %x, float %a) + ret float %b +} + +define float @maxnan_x_minnan_x_y(float %x, float %y) { +; CHECK-LABEL: @maxnan_x_minnan_x_y( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.minnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: [[B:%.*]] = call float @llvm.maxnan.f32(float [[X]], float [[A]]) +; CHECK-NEXT: ret float [[B]] +; + %a = call float @llvm.minnan.f32(float %x, float %y) + %b = call float @llvm.maxnan.f32(float %x, float %a) + ret float %b +} + +; PR37405 - https://bugs.llvm.org/show_bug.cgi?id=37405 + +define double @neg_neg(double %x, double %y) { +; CHECK-LABEL: @neg_neg( +; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.maxnan.f64(double [[X:%.*]], double [[Y:%.*]]) +; CHECK-NEXT: [[R:%.*]] = fsub double -0.000000e+00, [[TMP1]] +; CHECK-NEXT: ret double [[R]] +; + %negx = fsub double -0.0, %x + %negy = fsub double -0.0, %y + %r = call double @llvm.minnan.f64(double %negx, double %negy) + ret double %r +} + +; FMF is not required, but it should be propagated from the intrinsic (not the fnegs). +; Also, make sure this works with vectors. + +define <2 x double> @neg_neg_vec_fmf(<2 x double> %x, <2 x double> %y) { +; CHECK-LABEL: @neg_neg_vec_fmf( +; CHECK-NEXT: [[TMP1:%.*]] = call nnan ninf <2 x double> @llvm.maxnan.v2f64(<2 x double> [[X:%.*]], <2 x double> [[Y:%.*]]) +; CHECK-NEXT: [[R:%.*]] = fsub nnan ninf <2 x double> , [[TMP1]] +; CHECK-NEXT: ret <2 x double> [[R]] +; + %negx = fsub reassoc <2 x double> , %x + %negy = fsub fast <2 x double> , %y + %r = call nnan ninf <2 x double> @llvm.minnan.v2f64(<2 x double> %negx, <2 x double> %negy) + ret <2 x double> %r +} + +; 1 extra use of an intermediate value should still allow the fold, +; but 2 would require more instructions than we started with. + +declare void @use(double) +define double @neg_neg_extra_use_x(double %x, double %y) { +; CHECK-LABEL: @neg_neg_extra_use_x( +; CHECK-NEXT: [[NEGX:%.*]] = fsub double -0.000000e+00, [[X:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.maxnan.f64(double [[X]], double [[Y:%.*]]) +; CHECK-NEXT: [[R:%.*]] = fsub double -0.000000e+00, [[TMP1]] +; CHECK-NEXT: call void @use(double [[NEGX]]) +; CHECK-NEXT: ret double [[R]] +; + %negx = fsub double -0.0, %x + %negy = fsub double -0.0, %y + %r = call double @llvm.minnan.f64(double %negx, double %negy) + call void @use(double %negx) + ret double %r +} + +define double @neg_neg_extra_use_y(double %x, double %y) { +; CHECK-LABEL: @neg_neg_extra_use_y( +; CHECK-NEXT: [[NEGY:%.*]] = fsub double -0.000000e+00, [[Y:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.maxnan.f64(double [[X:%.*]], double [[Y]]) +; CHECK-NEXT: [[R:%.*]] = fsub double -0.000000e+00, [[TMP1]] +; CHECK-NEXT: call void @use(double [[NEGY]]) +; CHECK-NEXT: ret double [[R]] +; + %negx = fsub double -0.0, %x + %negy = fsub double -0.0, %y + %r = call double @llvm.minnan.f64(double %negx, double %negy) + call void @use(double %negy) + ret double %r +} + +define double @neg_neg_extra_use_x_and_y(double %x, double %y) { +; CHECK-LABEL: @neg_neg_extra_use_x_and_y( +; CHECK-NEXT: [[NEGX:%.*]] = fsub double -0.000000e+00, [[X:%.*]] +; CHECK-NEXT: [[NEGY:%.*]] = fsub double -0.000000e+00, [[Y:%.*]] +; CHECK-NEXT: [[R:%.*]] = call double @llvm.minnan.f64(double [[NEGX]], double [[NEGY]]) +; CHECK-NEXT: call void @use(double [[NEGX]]) +; CHECK-NEXT: call void @use(double [[NEGY]]) +; CHECK-NEXT: ret double [[R]] +; + %negx = fsub double -0.0, %x + %negy = fsub double -0.0, %y + %r = call double @llvm.minnan.f64(double %negx, double %negy) + call void @use(double %negx) + call void @use(double %negy) + ret double %r +} Index: test/Transforms/InstSimplify/floating-point-arithmetic.ll =================================================================== --- test/Transforms/InstSimplify/floating-point-arithmetic.ll +++ test/Transforms/InstSimplify/floating-point-arithmetic.ll @@ -790,6 +790,322 @@ ret float %val } +declare float @llvm.minnan.f32(float, float) +declare float @llvm.maxnan.f32(float, float) +declare double @llvm.minnan.f64(double, double) +declare double @llvm.maxnan.f64(double, double) +declare <2 x double> @llvm.minnan.v2f64(<2 x double>, <2 x double>) +declare <2 x double> @llvm.maxnan.v2f64(<2 x double>, <2 x double>) + +; From the LangRef for minnan/maxnan: +; "If either operand is a NaN, returns NaN." + +define double @maxnan_nan_op0(double %x) { +; CHECK-LABEL: @maxnan_nan_op0( +; CHECK-NEXT: ret double 0x7FF8000000000000 +; + %r = call double @llvm.maxnan.f64(double 0x7ff8000000000000, double %x) + ret double %r +} + +define double @maxnan_nan_op1(double %x) { +; CHECK-LABEL: @maxnan_nan_op1( +; CHECK-NEXT: ret double 0x7FF800000000DEAD +; + %r = call double @llvm.maxnan.f64(double %x, double 0x7ff800000000dead) + ret double %r +} + +define double @minnan_nan_op0(double %x) { +; CHECK-LABEL: @minnan_nan_op0( +; CHECK-NEXT: ret double 0x7FF8000DEAD00000 +; + %r = call double @llvm.minnan.f64(double 0x7ff8000dead00000, double %x) + ret double %r +} + +define double @minnan_nan_op1(double %x) { +; CHECK-LABEL: @minnan_nan_op1( +; CHECK-NEXT: ret double 0x7FF800DEAD00DEAD +; + %r = call double @llvm.minnan.f64(double %x, double 0x7ff800dead00dead) + ret double %r +} + +define <2 x double> @maxnan_nan_op0_vec(<2 x double> %x) { +; CHECK-LABEL: @maxnan_nan_op0_vec( +; CHECK-NEXT: ret <2 x double> +; + %r = call <2 x double> @llvm.maxnan.v2f64(<2 x double> , <2 x double> %x) + ret <2 x double> %r +} + +define <2 x double> @maxnan_nan_op1_vec(<2 x double> %x) { +; CHECK-LABEL: @maxnan_nan_op1_vec( +; CHECK-NEXT: ret <2 x double> +; + %r = call <2 x double> @llvm.maxnan.v2f64(<2 x double> %x, <2 x double> ) + ret <2 x double> %r +} + +define <2 x double> @minnan_nan_op0_vec(<2 x double> %x) { +; CHECK-LABEL: @minnan_nan_op0_vec( +; CHECK-NEXT: ret <2 x double> +; + %r = call <2 x double> @llvm.minnan.v2f64(<2 x double> , <2 x double> %x) + ret <2 x double> %r +} + +define <2 x double> @minnan_nan_op1_vec(<2 x double> %x) { +; CHECK-LABEL: @minnan_nan_op1_vec( +; CHECK-NEXT: ret <2 x double> +; + %r = call <2 x double> @llvm.minnan.v2f64(<2 x double> %x, <2 x double> ) + ret <2 x double> %r +} + +define float @maxnan_undef_op1(float %x) { +; CHECK-LABEL: @maxnan_undef_op1( +; CHECK-NEXT: ret float [[X:%.*]] +; + %val = call float @llvm.maxnan.f32(float %x, float undef) + ret float %val +} + +define float @maxnan_undef_op0(float %x) { +; CHECK-LABEL: @maxnan_undef_op0( +; CHECK-NEXT: ret float [[X:%.*]] +; + %val = call float @llvm.maxnan.f32(float undef, float %x) + ret float %val +} + +define float @minnan_undef_op1(float %x) { +; CHECK-LABEL: @minnan_undef_op1( +; CHECK-NEXT: ret float [[X:%.*]] +; + %val = call float @llvm.minnan.f32(float %x, float undef) + ret float %val +} + +define float @minnan_undef_op0(float %x) { +; CHECK-LABEL: @minnan_undef_op0( +; CHECK-NEXT: ret float [[X:%.*]] +; + %val = call float @llvm.minnan.f32(float undef, float %x) + ret float %val +} + +define float @minnan_undef_undef(float %x) { +; CHECK-LABEL: @minnan_undef_undef( +; CHECK-NEXT: ret float undef +; + %val = call float @llvm.minnan.f32(float undef, float undef) + ret float %val +} + +define float @maxnan_undef_undef(float %x) { +; CHECK-LABEL: @maxnan_undef_undef( +; CHECK-NEXT: ret float undef +; + %val = call float @llvm.maxnan.f32(float undef, float undef) + ret float %val +} + +define float @minnan_same_args(float %x) { +; CHECK-LABEL: @minnan_same_args( +; CHECK-NEXT: ret float [[X:%.*]] +; + %y = call float @llvm.minnan.f32(float %x, float %x) + ret float %y +} + +define float @maxnan_same_args(float %x) { +; CHECK-LABEL: @maxnan_same_args( +; CHECK-NEXT: ret float [[X:%.*]] +; + %y = call float @llvm.maxnan.f32(float %x, float %x) + ret float %y +} + +define float @minnan_x_minnan_x_y(float %x, float %y) { +; CHECK-LABEL: @minnan_x_minnan_x_y( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.minnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: ret float [[A]] +; + %a = call float @llvm.minnan.f32(float %x, float %y) + %b = call float @llvm.minnan.f32(float %x, float %a) + ret float %b +} + +define float @minnan_y_minnan_x_y(float %x, float %y) { +; CHECK-LABEL: @minnan_y_minnan_x_y( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.minnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: ret float [[A]] +; + %a = call float @llvm.minnan.f32(float %x, float %y) + %b = call float @llvm.minnan.f32(float %y, float %a) + ret float %b +} + +define float @minnan_x_y_minnan_x(float %x, float %y) { +; CHECK-LABEL: @minnan_x_y_minnan_x( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.minnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: ret float [[A]] +; + %a = call float @llvm.minnan.f32(float %x, float %y) + %b = call float @llvm.minnan.f32(float %a, float %x) + ret float %b +} + +define float @minnan_x_y_minnan_y(float %x, float %y) { +; CHECK-LABEL: @minnan_x_y_minnan_y( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.minnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: ret float [[A]] +; + %a = call float @llvm.minnan.f32(float %x, float %y) + %b = call float @llvm.minnan.f32(float %a, float %y) + ret float %b +} + +; negative test + +define float @minnan_z_minnan_x_y(float %x, float %y, float %z) { +; CHECK-LABEL: @minnan_z_minnan_x_y( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.minnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: [[B:%.*]] = call float @llvm.minnan.f32(float [[Z:%.*]], float [[A]]) +; CHECK-NEXT: ret float [[B]] +; + %a = call float @llvm.minnan.f32(float %x, float %y) + %b = call float @llvm.minnan.f32(float %z, float %a) + ret float %b +} + +; negative test + +define float @minnan_x_y_minnan_z(float %x, float %y, float %z) { +; CHECK-LABEL: @minnan_x_y_minnan_z( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.minnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: [[B:%.*]] = call float @llvm.minnan.f32(float [[A]], float [[Z:%.*]]) +; CHECK-NEXT: ret float [[B]] +; + %a = call float @llvm.minnan.f32(float %x, float %y) + %b = call float @llvm.minnan.f32(float %a, float %z) + ret float %b +} + +; minnan(X, -INF) --> -INF + +define float @minnan_neginf(float %x) { +; CHECK-LABEL: @minnan_neginf( +; CHECK-NEXT: ret float 0xFFF0000000000000 +; + %val = call float @llvm.minnan.f32(float %x, float 0xFFF0000000000000) + ret float %val +} + +define <2 x double> @minnan_neginf_commute_vec(<2 x double> %x) { +; CHECK-LABEL: @minnan_neginf_commute_vec( +; CHECK-NEXT: ret <2 x double> +; + %r = call <2 x double> @llvm.minnan.v2f64(<2 x double> , <2 x double> %x) + ret <2 x double> %r +} + +; negative test + +define float @minnan_inf(float %x) { +; CHECK-LABEL: @minnan_inf( +; CHECK-NEXT: [[VAL:%.*]] = call float @llvm.minnan.f32(float 0x7FF0000000000000, float [[X:%.*]]) +; CHECK-NEXT: ret float [[VAL]] +; + %val = call float @llvm.minnan.f32(float 0x7FF0000000000000, float %x) + ret float %val +} +define float @maxnan_x_maxnan_x_y(float %x, float %y) { +; CHECK-LABEL: @maxnan_x_maxnan_x_y( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.maxnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: ret float [[A]] +; + %a = call float @llvm.maxnan.f32(float %x, float %y) + %b = call float @llvm.maxnan.f32(float %x, float %a) + ret float %b +} + +define float @maxnan_y_maxnan_x_y(float %x, float %y) { +; CHECK-LABEL: @maxnan_y_maxnan_x_y( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.maxnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: ret float [[A]] +; + %a = call float @llvm.maxnan.f32(float %x, float %y) + %b = call float @llvm.maxnan.f32(float %y, float %a) + ret float %b +} + +define float @maxnan_x_y_maxnan_x(float %x, float %y) { +; CHECK-LABEL: @maxnan_x_y_maxnan_x( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.maxnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: ret float [[A]] +; + %a = call float @llvm.maxnan.f32(float %x, float %y) + %b = call float @llvm.maxnan.f32(float %a, float %x) + ret float %b +} + +define float @maxnan_x_y_maxnan_y(float %x, float %y) { +; CHECK-LABEL: @maxnan_x_y_maxnan_y( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.maxnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: ret float [[A]] +; + %a = call float @llvm.maxnan.f32(float %x, float %y) + %b = call float @llvm.maxnan.f32(float %a, float %y) + ret float %b +} + +; negative test + +define float @maxnan_z_maxnan_x_y(float %x, float %y, float %z) { +; CHECK-LABEL: @maxnan_z_maxnan_x_y( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.maxnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: [[B:%.*]] = call float @llvm.maxnan.f32(float [[Z:%.*]], float [[A]]) +; CHECK-NEXT: ret float [[B]] +; + %a = call float @llvm.maxnan.f32(float %x, float %y) + %b = call float @llvm.maxnan.f32(float %z, float %a) + ret float %b +} + +; negative test + +define float @maxnan_x_y_maxnan_z(float %x, float %y, float %z) { +; CHECK-LABEL: @maxnan_x_y_maxnan_z( +; CHECK-NEXT: [[A:%.*]] = call float @llvm.maxnan.f32(float [[X:%.*]], float [[Y:%.*]]) +; CHECK-NEXT: [[B:%.*]] = call float @llvm.maxnan.f32(float [[A]], float [[Z:%.*]]) +; CHECK-NEXT: ret float [[B]] +; + %a = call float @llvm.maxnan.f32(float %x, float %y) + %b = call float @llvm.maxnan.f32(float %a, float %z) + ret float %b +} + +; maxnan(X, INF) --> INF + +define <2 x double> @maxnan_inf(<2 x double> %x) { +; CHECK-LABEL: @maxnan_inf( +; CHECK-NEXT: ret <2 x double> +; + %val = call <2 x double> @llvm.maxnan.v2f64(<2 x double> %x, <2 x double>) + ret <2 x double> %val +} + +define float @maxnan_inf_commute(float %x) { +; CHECK-LABEL: @maxnan_inf_commute( +; CHECK-NEXT: ret float 0x7FF0000000000000 +; + %val = call float @llvm.maxnan.f32(float 0x7FF0000000000000, float %x) + ret float %val +} + ; Y - (Y - X) --> X define float @fsub_fsub_common_op(float %x, float %y) { Index: test/Transforms/InstSimplify/floating-point-compare.ll =================================================================== --- test/Transforms/InstSimplify/floating-point-compare.ll +++ test/Transforms/InstSimplify/floating-point-compare.ll @@ -179,6 +179,7 @@ declare float @llvm.exp.f32(float) declare float @llvm.minnum.f32(float, float) declare float @llvm.maxnum.f32(float, float) +declare float @llvm.maxnan.f32(float, float) declare double @llvm.exp2.f64(double) declare float @llvm.fma.f32(float,float,float) @@ -282,6 +283,18 @@ ret i1 %uge } +; But using maxnan, we can simplify, since the NaN would be propagated + +define i1 @orderedLessZeroMaxNaN(float, float) { +; CHECK-LABEL: @orderedLessZeroMaxNaN( +; CHECK-NEXT: ret i1 true +; + %a = call float @llvm.exp.f32(float %0) + %b = call float @llvm.maxnan.f32(float %a, float %1) + %uge = fcmp uge float %b, 0.000000e+00 + ret i1 %uge +} + define i1 @known_positive_olt_with_negative_constant(double %a) { ; CHECK-LABEL: @known_positive_olt_with_negative_constant( ; CHECK-NEXT: ret i1 false Index: test/Transforms/LICM/hoist-round.ll =================================================================== --- test/Transforms/LICM/hoist-round.ll +++ test/Transforms/LICM/hoist-round.ll @@ -4,8 +4,8 @@ target datalayout = "E-m:e-p:32:32-i8:8:8-i16:16:16-i64:32:32-f64:32:32-v64:32:32-v128:32:32-a0:0:32-n32" ; This test verifies that ceil, floor, nearbyint, trunc, rint, round, -; copysign, minnum, maxnum and fabs intrinsics are considered safe -; to speculate. +; copysign, minnum, maxnum minnan, maxnan, and fabs intrinsics are +; considered safe to speculate. ; CHECK-LABEL: @test ; CHECK: call float @llvm.ceil.f32 @@ -41,8 +41,10 @@ %tmp.8 = call float @llvm.copysign.f32(float %tmp.7, float %arg2) %tmp.9 = call float @llvm.minnum.f32(float %tmp.8, float %arg2) %tmp.10 = call float @llvm.maxnum.f32(float %tmp.9, float %arg2) - %tmp.11 = call float @llvm.powi.f32(float %tmp.10, i32 4) - call void @consume(float %tmp.11) + %tmp.11 = call float @llvm.minnan.f32(float %tmp.10, float %arg2) + %tmp.12 = call float @llvm.maxnan.f32(float %tmp.11, float %arg2) + %tmp.13 = call float @llvm.powi.f32(float %tmp.12, i32 4) + call void @consume(float %tmp.13) %IND.new = add i32 %IND, 1 br label %for.head @@ -62,4 +64,6 @@ declare float @llvm.copysign.f32(float, float) declare float @llvm.minnum.f32(float, float) declare float @llvm.maxnum.f32(float, float) +declare float @llvm.minnan.f32(float, float) +declare float @llvm.maxnan.f32(float, float) declare float @llvm.powi.f32(float, i32) Index: test/Transforms/SimplifyCFG/speculate-math.ll =================================================================== --- test/Transforms/SimplifyCFG/speculate-math.ll +++ test/Transforms/SimplifyCFG/speculate-math.ll @@ -7,6 +7,8 @@ declare float @llvm.fabs.f32(float) nounwind readonly declare float @llvm.minnum.f32(float, float) nounwind readonly declare float @llvm.maxnum.f32(float, float) nounwind readonly +declare float @llvm.minnan.f32(float, float) nounwind readonly +declare float @llvm.maxnan.f32(float, float) nounwind readonly ; ALL-LABEL: @fdiv_test( ; EXPENSIVE: select i1 %cmp, double %div, double 0.0 @@ -127,3 +129,37 @@ store float %cond.i, float addrspace(1)* %out, align 4 ret void } + +; ALL-LABEL: @minnan_test( +; ALL: select +define void @minnan_test(float addrspace(1)* noalias nocapture %out, float %a, float %b) nounwind { +entry: + %cmp.i = fcmp olt float %a, 0.000000e+00 + br i1 %cmp.i, label %test_minnan.exit, label %cond.else.i + +cond.else.i: ; preds = %entry + %0 = tail call float @llvm.minnan.f32(float %a, float %b) nounwind readnone + br label %test_minnan.exit + +test_minnan.exit: ; preds = %cond.else.i, %entry + %cond.i = phi float [ %0, %cond.else.i ], [ 0x7FF8000000000000, %entry ] + store float %cond.i, float addrspace(1)* %out, align 4 + ret void +} + +; ALL-LABEL: @maxnan_test( +; ALL: select +define void @maxnan_test(float addrspace(1)* noalias nocapture %out, float %a, float %b) nounwind { +entry: + %cmp.i = fcmp olt float %a, 0.000000e+00 + br i1 %cmp.i, label %test_maxnan.exit, label %cond.else.i + +cond.else.i: ; preds = %entry + %0 = tail call float @llvm.maxnan.f32(float %a, float %b) nounwind readnone + br label %test_maxnan.exit + +test_maxnan.exit: ; preds = %cond.else.i, %entry + %cond.i = phi float [ %0, %cond.else.i ], [ 0x7FF8000000000000, %entry ] + store float %cond.i, float addrspace(1)* %out, align 4 + ret void +}