diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -4959,25 +4959,28 @@ if (Constant *C = simplifyFPOp({Op0, Op1}, FMF, Q, ExBehavior, Rounding)) return C; - if (!isDefaultFPEnvironment(ExBehavior, Rounding)) - return nullptr; - // fsub X, +0 ==> X - if (match(Op1, m_PosZeroFP())) - return Op0; + if (ExBehavior != fp::ebStrict || FMF.noNaNs()) + if (match(Op1, m_PosZeroFP())) + return Op0; // fsub X, -0 ==> X, when we know X is not -0 - if (match(Op1, m_NegZeroFP()) && - (FMF.noSignedZeros() || CannotBeNegativeZero(Op0, Q.TLI))) + if (ExBehavior != fp::ebStrict || FMF.noNaNs()) + if (match(Op1, m_NegZeroFP()) && + (FMF.noSignedZeros() || CannotBeNegativeZero(Op0, Q.TLI))) return Op0; // fsub -0.0, (fsub -0.0, X) ==> X // fsub -0.0, (fneg X) ==> X Value *X; - if (match(Op0, m_NegZeroFP()) && - match(Op1, m_FNeg(m_Value(X)))) + if (ExBehavior != fp::ebStrict || FMF.noNaNs()) + if (match(Op0, m_NegZeroFP()) && + match(Op1, m_FNeg(m_Value(X)))) return X; + if (!isDefaultFPEnvironment(ExBehavior, Rounding)) + return nullptr; + // fsub 0.0, (fsub 0.0, X) ==> X if signed zeros are ignored. // fsub 0.0, (fneg X) ==> X if signed zeros are ignored. if (FMF.noSignedZeros() && match(Op0, m_AnyZeroFP()) && diff --git a/llvm/test/Transforms/InstSimplify/strictfp-fsub.ll b/llvm/test/Transforms/InstSimplify/strictfp-fsub.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstSimplify/strictfp-fsub.ll @@ -0,0 +1,472 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -instsimplify -S | FileCheck %s + +; +; constrained fsub +; + +; +; fsub X, +0 ==> X +; + +define float @fsub_x_p0_defaultenv(float %a) #0 { +; CHECK-LABEL: @fsub_x_p0_defaultenv( +; CHECK-NEXT: ret float [[A:%.*]] +; + %ret = call float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %ret +} + +define <2 x float> @fsub_vec_x_p0_defaultenv(<2 x float> %a) #0 { +; CHECK-LABEL: @fsub_vec_x_p0_defaultenv( +; CHECK-NEXT: ret <2 x float> [[A:%.*]] +; + %ret = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> %a, <2 x float>, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret <2 x float> %ret +} + +define float @fsub_x_p0_ebmaytrap(float %a) #0 { +; CHECK-LABEL: @fsub_x_p0_ebmaytrap( +; CHECK-NEXT: ret float [[A:%.*]] +; + %ret = call float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #0 + ret float %ret +} + +define <2 x float> @fsub_vec_x_p0_ebmaytrap(<2 x float> %a) #0 { +; CHECK-LABEL: @fsub_vec_x_p0_ebmaytrap( +; CHECK-NEXT: ret <2 x float> [[A:%.*]] +; + %ret = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> %a, <2 x float>, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #0 + ret <2 x float> %ret +} + +define float @fsub_x_p0_ebstrict(float %a) #0 { +; CHECK-LABEL: @fsub_x_p0_ebstrict( +; CHECK-NEXT: [[RET:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[A:%.*]], float 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0:[0-9]+]] +; CHECK-NEXT: ret float [[RET]] +; + %ret = call float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret float %ret +} + +define <2 x float> @fsub_vec_x_p0_ebstrict(<2 x float> %a) #0 { +; CHECK-LABEL: @fsub_vec_x_p0_ebstrict( +; CHECK-NEXT: [[RET:%.*]] = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> [[A:%.*]], <2 x float> zeroinitializer, metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret <2 x float> [[RET]] +; + %ret = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> %a, <2 x float>, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret <2 x float> %ret +} + +define float @fsub_nnan_x_p0_ebstrict(float %a) #0 { +; CHECK-LABEL: @fsub_nnan_x_p0_ebstrict( +; CHECK-NEXT: [[RET:%.*]] = call nnan float @llvm.experimental.constrained.fsub.f32(float [[A:%.*]], float 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[A]] +; + %ret = call nnan float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret float %ret +} + +define <2 x float> @fsub_vec_nnan_x_p0_ebstrict(<2 x float> %a) #0 { +; CHECK-LABEL: @fsub_vec_nnan_x_p0_ebstrict( +; CHECK-NEXT: [[RET:%.*]] = call nnan <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> [[A:%.*]], <2 x float> zeroinitializer, metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret <2 x float> [[A]] +; + %ret = call nnan <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> %a, <2 x float>, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret <2 x float> %ret +} + +; Test with a fast math flag set but that flag is not "nnan". +define float @fsub_ninf_x_p0_ebstrict(float %a) #0 { +; CHECK-LABEL: @fsub_ninf_x_p0_ebstrict( +; CHECK-NEXT: [[RET:%.*]] = call ninf float @llvm.experimental.constrained.fsub.f32(float [[A:%.*]], float 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[RET]] +; + %ret = call ninf float @llvm.experimental.constrained.fsub.f32(float %a, float 0.0, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret float %ret +} + +; Test with a fast math flag set but that flag is not "nnan". +define <2 x float> @fsub_vec_ninf_x_p0_ebstrict(<2 x float> %a) #0 { +; CHECK-LABEL: @fsub_vec_ninf_x_p0_ebstrict( +; CHECK-NEXT: [[RET:%.*]] = call ninf <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> [[A:%.*]], <2 x float> zeroinitializer, metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret <2 x float> [[RET]] +; + %ret = call ninf <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> %a, <2 x float>, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret <2 x float> %ret +} + +; +; fsub X, -0 ==> X, when we know X is not -0 +; (fast math flag: nsz) +; + +define float @fold_fsub_nsz_x_n0_defaultenv(float %a) #0 { +; CHECK-LABEL: @fold_fsub_nsz_x_n0_defaultenv( +; CHECK-NEXT: ret float [[A:%.*]] +; + %sub = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %sub +} + +define <2 x float> @fold_fsub_vec_nsz_x_n0_defaultenv(<2 x float> %a) #0 { +; CHECK-LABEL: @fold_fsub_vec_nsz_x_n0_defaultenv( +; CHECK-NEXT: ret <2 x float> [[A:%.*]] +; + %sub = call nsz <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> %a, <2 x float> , metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret <2 x float> %sub +} + +define float @fold_fsub_nsz_x_n0_ebmaytrap(float %a) #0 { +; CHECK-LABEL: @fold_fsub_nsz_x_n0_ebmaytrap( +; CHECK-NEXT: ret float [[A:%.*]] +; + %sub = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #0 + ret float %sub +} + +define <2 x float> @fold_fsub_vec_nsz_x_n0_ebmaytrap(<2 x float> %a) #0 { +; CHECK-LABEL: @fold_fsub_vec_nsz_x_n0_ebmaytrap( +; CHECK-NEXT: ret <2 x float> [[A:%.*]] +; + %sub = call nsz <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> %a, <2 x float> , metadata !"round.tonearest", metadata !"fpexcept.maytrap") #0 + ret <2 x float> %sub +} + +define float @fold_fsub_nsz_x_n0_ebstrict(float %a) #0 { +; CHECK-LABEL: @fold_fsub_nsz_x_n0_ebstrict( +; CHECK-NEXT: [[SUB:%.*]] = call nsz float @llvm.experimental.constrained.fsub.f32(float [[A:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[SUB]] +; + %sub = call nsz float @llvm.experimental.constrained.fsub.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret float %sub +} + +define <2 x float> @fold_fsub_vec_nsz_x_n0_ebstrict(<2 x float> %a) #0 { +; CHECK-LABEL: @fold_fsub_vec_nsz_x_n0_ebstrict( +; CHECK-NEXT: [[SUB:%.*]] = call nsz <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> [[A:%.*]], <2 x float> , metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret <2 x float> [[SUB]] +; + %sub = call nsz <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> %a, <2 x float> , metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret <2 x float> %sub +} + +define float @fold_fsub_nsz_nnan_x_n0_ebstrict(float %a) #0 { +; CHECK-LABEL: @fold_fsub_nsz_nnan_x_n0_ebstrict( +; CHECK-NEXT: [[SUB:%.*]] = call nnan nsz float @llvm.experimental.constrained.fsub.f32(float [[A:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[A]] +; + %sub = call nsz nnan float @llvm.experimental.constrained.fsub.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret float %sub +} + +define <2 x float> @fold_fsub_vec_nsz_nnan_x_n0_ebstrict(<2 x float> %a) #0 { +; CHECK-LABEL: @fold_fsub_vec_nsz_nnan_x_n0_ebstrict( +; CHECK-NEXT: [[SUB:%.*]] = call nnan nsz <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> [[A:%.*]], <2 x float> , metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret <2 x float> [[A]] +; + %sub = call nsz nnan <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> %a, <2 x float> , metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret <2 x float> %sub +} + +; +; fsub X, -0 ==> X, when we know X is not -0 +; (No "nsz" flags) +; + +define float @fold_fsub_fabs_x_n0_defaultenv(float %a) #0 { +; CHECK-LABEL: @fold_fsub_fabs_x_n0_defaultenv( +; CHECK-NEXT: [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) +; CHECK-NEXT: ret float [[ABSA]] +; + %absa = call float @llvm.fabs.f32(float %a) + %sub = call float @llvm.experimental.constrained.fsub.f32(float %absa, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %sub +} + +define <2 x float> @fold_fsub_vec_fabs_x_n0_defaultenv(<2 x float> %a) #0 { +; CHECK-LABEL: @fold_fsub_vec_fabs_x_n0_defaultenv( +; CHECK-NEXT: [[ABSA:%.*]] = call <2 x float> @llvm.fabs.v2f32(<2 x float> [[A:%.*]]) +; CHECK-NEXT: ret <2 x float> [[ABSA]] +; + %absa = call <2 x float> @llvm.fabs.v2f32(<2 x float> %a) + %sub = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> %absa, <2 x float> , metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret <2 x float> %sub +} + +define float @fold_fsub_fabs_x_n0_ebmaytrap(float %a) #0 { +; CHECK-LABEL: @fold_fsub_fabs_x_n0_ebmaytrap( +; CHECK-NEXT: [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) +; CHECK-NEXT: ret float [[ABSA]] +; + %absa = call float @llvm.fabs.f32(float %a) + %sub = call float @llvm.experimental.constrained.fsub.f32(float %absa, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #0 + ret float %sub +} + +define <2 x float> @fold_fsub_vec_fabs_x_n0_ebmaytrap(<2 x float> %a) #0 { +; CHECK-LABEL: @fold_fsub_vec_fabs_x_n0_ebmaytrap( +; CHECK-NEXT: [[ABSA:%.*]] = call <2 x float> @llvm.fabs.v2f32(<2 x float> [[A:%.*]]) +; CHECK-NEXT: ret <2 x float> [[ABSA]] +; + %absa = call <2 x float> @llvm.fabs.v2f32(<2 x float> %a) + %sub = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> %absa, <2 x float> , metadata !"round.tonearest", metadata !"fpexcept.maytrap") #0 + ret <2 x float> %sub +} + +define float @fold_fsub_fabs_x_n0_ebstrict(float %a) #0 { +; CHECK-LABEL: @fold_fsub_fabs_x_n0_ebstrict( +; CHECK-NEXT: [[SUB:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[A:%.*]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[SUB]] +; + %absa = call float @llvm.fabs.f32(float %a) + %sub = call float @llvm.experimental.constrained.fsub.f32(float %a, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret float %sub +} + +define <2 x float> @fold_fsub_vec_fabs_x_n0_ebstrict(<2 x float> %a) #0 { +; CHECK-LABEL: @fold_fsub_vec_fabs_x_n0_ebstrict( +; CHECK-NEXT: [[ABSA:%.*]] = call <2 x float> @llvm.fabs.v2f32(<2 x float> [[A:%.*]]) +; CHECK-NEXT: [[SUB:%.*]] = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> [[ABSA]], <2 x float> , metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret <2 x float> [[SUB]] +; + %absa = call <2 x float> @llvm.fabs.v2f32(<2 x float> %a) + %sub = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> %absa, <2 x float> , metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret <2 x float> %sub +} + +define float @fold_fsub_fabs_nnan_x_n0_ebstrict(float %a) #0 { +; CHECK-LABEL: @fold_fsub_fabs_nnan_x_n0_ebstrict( +; CHECK-NEXT: [[ABSA:%.*]] = call float @llvm.fabs.f32(float [[A:%.*]]) +; CHECK-NEXT: [[SUB:%.*]] = call nnan float @llvm.experimental.constrained.fsub.f32(float [[ABSA]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[ABSA]] +; + %absa = call float @llvm.fabs.f32(float %a) + %sub = call nnan float @llvm.experimental.constrained.fsub.f32(float %absa, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret float %sub +} + +define <2 x float> @fold_fsub_vec_fabs_nnan_x_n0_ebstrict(<2 x float> %a) #0 { +; CHECK-LABEL: @fold_fsub_vec_fabs_nnan_x_n0_ebstrict( +; CHECK-NEXT: [[ABSA:%.*]] = call <2 x float> @llvm.fabs.v2f32(<2 x float> [[A:%.*]]) +; CHECK-NEXT: [[SUB:%.*]] = call nnan <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> [[ABSA]], <2 x float> , metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret <2 x float> [[ABSA]] +; + %absa = call <2 x float> @llvm.fabs.v2f32(<2 x float> %a) + %sub = call nnan <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> %absa, <2 x float> , metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret <2 x float> %sub +} + +; TODO: This can't fire currently due to missing constrained support: +define float @fold_fsub_sitofp_x_n0_defaultenv(i32 %a) #0 { +; CHECK-LABEL: @fold_fsub_sitofp_x_n0_defaultenv( +; CHECK-NEXT: [[FPA:%.*]] = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 [[A:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]] +; CHECK-NEXT: [[SUB:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float [[FPA]], float -0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]] +; CHECK-NEXT: ret float [[SUB]] +; + %fpa = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + %sub = call float @llvm.experimental.constrained.fsub.f32(float %fpa, float -0.0, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %sub +} + +; TODO: This can't fire currently due to missing constrained support: +define <2 x float> @fold_fsub_vec_sitofp_x_n0_defaultenv(<2 x i32> %a) #0 { +; CHECK-LABEL: @fold_fsub_vec_sitofp_x_n0_defaultenv( +; CHECK-NEXT: [[FPA:%.*]] = call <2 x float> @llvm.experimental.constrained.sitofp.v2f32.v2i32(<2 x i32> [[A:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]] +; CHECK-NEXT: [[SUB:%.*]] = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> [[FPA]], <2 x float> , metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]] +; CHECK-NEXT: ret <2 x float> [[SUB]] +; + %fpa = call <2 x float> @llvm.experimental.constrained.sitofp.v2f32.v2i32(<2 x i32> %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + %sub = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> %fpa, <2 x float> , metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret <2 x float> %sub +} + +; +; fsub -0.0, (fneg X) ==> X +; + +define float @fsub_fneg_n0_fnX_defaultenv(float %a) #0 { +; CHECK-LABEL: @fsub_fneg_n0_fnX_defaultenv( +; CHECK-NEXT: ret float [[A:%.*]] +; + %nega = fneg float %a + %ret = call float @llvm.experimental.constrained.fsub.f32(float -0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %ret +} + +define <2 x float> @fsub_fneg_vec_n0_fnX_defaultenv(<2 x float> %a) #0 { +; CHECK-LABEL: @fsub_fneg_vec_n0_fnX_defaultenv( +; CHECK-NEXT: ret <2 x float> [[A:%.*]] +; + %nega = fneg <2 x float> %a + %ret = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> %nega, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret <2 x float> %ret +} + +define float @fsub_fneg_n0_fnX_ebmaytrap(float %a) #0 { +; CHECK-LABEL: @fsub_fneg_n0_fnX_ebmaytrap( +; CHECK-NEXT: ret float [[A:%.*]] +; + %nega = fneg float %a + %ret = call float @llvm.experimental.constrained.fsub.f32(float -0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #0 + ret float %ret +} + +define <2 x float> @fsub_fneg_vec_n0_fnX_ebmaytrap(<2 x float> %a) #0 { +; CHECK-LABEL: @fsub_fneg_vec_n0_fnX_ebmaytrap( +; CHECK-NEXT: ret <2 x float> [[A:%.*]] +; + %nega = fneg <2 x float> %a + %ret = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> %nega, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #0 + ret <2 x float> %ret +} + +define float @fsub_fneg_n0_fnX_ebstrict(float %a) #0 { +; CHECK-LABEL: @fsub_fneg_n0_fnX_ebstrict( +; CHECK-NEXT: [[NEGA:%.*]] = fneg float [[A:%.*]] +; CHECK-NEXT: [[RET:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float -0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[RET]] +; + %nega = fneg float %a + %ret = call float @llvm.experimental.constrained.fsub.f32(float -0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret float %ret +} + +define <2 x float> @fsub_fneg_vec_n0_fnX_ebstrict(<2 x float> %a) #0 { +; CHECK-LABEL: @fsub_fneg_vec_n0_fnX_ebstrict( +; CHECK-NEXT: [[NEGA:%.*]] = fneg <2 x float> [[A:%.*]] +; CHECK-NEXT: [[RET:%.*]] = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret <2 x float> [[RET]] +; + %nega = fneg <2 x float> %a + %ret = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> %nega, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret <2 x float> %ret +} + +define float @fsub_fneg_nnan_n0_fnX_ebstrict(float %a) #0 { +; CHECK-LABEL: @fsub_fneg_nnan_n0_fnX_ebstrict( +; CHECK-NEXT: [[NEGA:%.*]] = fneg float [[A:%.*]] +; CHECK-NEXT: [[RET:%.*]] = call nnan float @llvm.experimental.constrained.fsub.f32(float -0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[A]] +; + %nega = fneg float %a + %ret = call nnan float @llvm.experimental.constrained.fsub.f32(float -0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret float %ret +} + +define <2 x float> @fsub_fneg_vec_nnan_n0_fnX_ebstrict(<2 x float> %a) #0 { +; CHECK-LABEL: @fsub_fneg_vec_nnan_n0_fnX_ebstrict( +; CHECK-NEXT: [[NEGA:%.*]] = fneg <2 x float> [[A:%.*]] +; CHECK-NEXT: [[RET:%.*]] = call nnan <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret <2 x float> [[A]] +; + %nega = fneg <2 x float> %a + %ret = call nnan <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> %nega, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret <2 x float> %ret +} + +; +; fsub -0.0, (fsub -0.0, X) ==> X +; TODO: These generally don't fire because m_FNeg doesn't know about +; TODO: the constrained intrinsics. +; + +define float @fsub_fsub_n0_fnX_defaultenv(float %a) #0 { +; CHECK-LABEL: @fsub_fsub_n0_fnX_defaultenv( +; CHECK-NEXT: [[NEGA:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float -0.000000e+00, float [[A:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]] +; CHECK-NEXT: [[RET:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float -0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]] +; CHECK-NEXT: ret float [[RET]] +; + %nega = call float @llvm.experimental.constrained.fsub.f32(float -0.0, float %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + %ret = call float @llvm.experimental.constrained.fsub.f32(float -0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret float %ret +} + +define <2 x float> @fsub_fsub_vec_n0_fnX_defaultenv(<2 x float> %a) #0 { +; CHECK-LABEL: @fsub_fsub_vec_n0_fnX_defaultenv( +; CHECK-NEXT: [[NEGA:%.*]] = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> [[A:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]] +; CHECK-NEXT: [[RET:%.*]] = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]] +; CHECK-NEXT: ret <2 x float> [[RET]] +; + %nega = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> %a, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + %ret = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> %nega, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + ret <2 x float> %ret +} + +define float @fsub_fsub_n0_fnX_ebmaytrap(float %a) #0 { +; CHECK-LABEL: @fsub_fsub_n0_fnX_ebmaytrap( +; CHECK-NEXT: [[NEGA:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float -0.000000e+00, float [[A:%.*]], metadata !"round.tonearest", metadata !"fpexcept.maytrap") #[[ATTR0]] +; CHECK-NEXT: [[RET:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float -0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.maytrap") #[[ATTR0]] +; CHECK-NEXT: ret float [[RET]] +; + %nega = call float @llvm.experimental.constrained.fsub.f32(float -0.0, float %a, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #0 + %ret = call float @llvm.experimental.constrained.fsub.f32(float -0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #0 + ret float %ret +} + +define <2 x float> @fsub_fsub_vec_n0_fnX_ebmaytrap(<2 x float> %a) #0 { +; CHECK-LABEL: @fsub_fsub_vec_n0_fnX_ebmaytrap( +; CHECK-NEXT: [[NEGA:%.*]] = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> [[A:%.*]], metadata !"round.tonearest", metadata !"fpexcept.maytrap") #[[ATTR0]] +; CHECK-NEXT: [[RET:%.*]] = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.maytrap") #[[ATTR0]] +; CHECK-NEXT: ret <2 x float> [[RET]] +; + %nega = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> %a, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #0 + %ret = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> %nega, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #0 + ret <2 x float> %ret +} + +define float @fsub_fsub_n0_fnX_ebstrict(float %a) #0 { +; CHECK-LABEL: @fsub_fsub_n0_fnX_ebstrict( +; CHECK-NEXT: [[NEGA:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float -0.000000e+00, float [[A:%.*]], metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: [[RET:%.*]] = call float @llvm.experimental.constrained.fsub.f32(float -0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[RET]] +; + %nega = call float @llvm.experimental.constrained.fsub.f32(float -0.0, float %a, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + %ret = call float @llvm.experimental.constrained.fsub.f32(float -0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret float %ret +} + +define <2 x float> @fsub_fsub_vec_n0_fnX_ebstrict(<2 x float> %a) #0 { +; CHECK-LABEL: @fsub_fsub_vec_n0_fnX_ebstrict( +; CHECK-NEXT: [[NEGA:%.*]] = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> [[A:%.*]], metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: [[RET:%.*]] = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret <2 x float> [[RET]] +; + %nega = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> %a, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + %ret = call <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> %nega, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret <2 x float> %ret +} + +define float @fsub_fsub_nnan_n0_fnX_ebstrict(float %a) #0 { +; CHECK-LABEL: @fsub_fsub_nnan_n0_fnX_ebstrict( +; CHECK-NEXT: [[NEGA:%.*]] = call nnan float @llvm.experimental.constrained.fsub.f32(float -0.000000e+00, float [[A:%.*]], metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: [[RET:%.*]] = call nnan float @llvm.experimental.constrained.fsub.f32(float -0.000000e+00, float [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret float [[RET]] +; + %nega = call nnan float @llvm.experimental.constrained.fsub.f32(float -0.0, float %a, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + %ret = call nnan float @llvm.experimental.constrained.fsub.f32(float -0.0, float %nega, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret float %ret +} + +define <2 x float> @fsub_fsub_vec_nnan_n0_fnX_ebstrict(<2 x float> %a) #0 { +; CHECK-LABEL: @fsub_fsub_vec_nnan_n0_fnX_ebstrict( +; CHECK-NEXT: [[NEGA:%.*]] = call nnan <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> [[A:%.*]], metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: [[RET:%.*]] = call nnan <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> [[NEGA]], metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]] +; CHECK-NEXT: ret <2 x float> [[RET]] +; + %nega = call nnan <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> %a, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + %ret = call nnan <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float> , <2 x float> %nega, metadata !"round.tonearest", metadata !"fpexcept.strict") #0 + ret <2 x float> %ret +} + +declare float @llvm.fabs.f32(float) +declare <2 x float> @llvm.fabs.v2f32(<2 x float>) + +declare float @llvm.experimental.constrained.fsub.f32(float, float, metadata, metadata) #0 +declare <2 x float> @llvm.experimental.constrained.fsub.v2f32(<2 x float>, <2 x float>, metadata, metadata) #0 + +declare float @llvm.experimental.constrained.sitofp.f32.i32(i32, metadata, metadata) #0 +declare <2 x float> @llvm.experimental.constrained.sitofp.v2f32.v2i32(<2 x i32>, metadata, metadata) #0 + +attributes #0 = { strictfp }