return nullptr;
}
+/// \returns true if the test performed by llvm.is.fpclass(x, \p Mask) is
+/// equivalent to fcmp oeq x, 0.0 with the floating-point environment assumed
+/// for \p F for type \p Ty
+static bool fpclassTestIsFCmp0(FPClassTest Mask, const Function &F, Type *Ty) {
+ if (Mask == fcZero)
+ return F.getDenormalMode(Ty->getScalarType()->getFltSemantics()).Input ==
+ DenormalMode::IEEE;
+
+ if (Mask == (fcZero | fcSubnormal)) {
+ DenormalMode::DenormalModeKind InputMode =
+ F.getDenormalMode(Ty->getScalarType()->getFltSemantics()).Input;
+ return InputMode == DenormalMode::PreserveSign ||
+ InputMode == DenormalMode::PositiveZero;
+ }
+
+ return false;
+}
+
Instruction *InstCombinerImpl::foldIntrinsicIsFPClass(IntrinsicInst &II) {
Value *Src0 = II.getArgOperand(0);
Value *Src1 = II.getArgOperand(1);
const ConstantInt *CMask = cast<ConstantInt>(Src1);
FPClassTest Mask = static_cast<FPClassTest>(CMask->getZExtValue());
-
+ FPClassTest InvertedMask = ~Mask;
const bool IsStrict = II.isStrictFP();
Value *FNegSrc;
return replaceInstUsesWith(II, FCmp);
}
+ if (!IsStrict &&
+ fpclassTestIsFCmp0(static_cast<FPClassTest>(Mask),
+ *II.getParent()->getParent(), Src0->getType())) {
+ // Equivalent of == 0.
+ Value *FCmp =
+ Builder.CreateFCmpOEQ(Src0, ConstantFP::get(Src0->getType(), 0.0));
+
+ FCmp->takeName(&II);
+ return replaceInstUsesWith(II, FCmp);
+ }
+
+ if (!IsStrict &&
+ fpclassTestIsFCmp0(static_cast<FPClassTest>(InvertedMask),
+ *II.getParent()->getParent(), Src0->getType())) {
+ // Equivalent of !(x == 0).
+ Value *FCmp =
+ Builder.CreateFCmpUNE(Src0, ConstantFP::get(Src0->getType(), 0.0));
+
+ FCmp->takeName(&II);
+ return replaceInstUsesWith(II, FCmp);
+ }
+
// fp_class (nnan x), qnan|snan|other -> fp_class (nnan x), other
if ((Mask & fcNan) && isKnownNeverNaN(Src0, &getTargetLibraryInfo())) {
II.setArgOperand(1, ConstantInt::get(Src1->getType(), Mask & ~fcNan));
define i1 @test_class_is_p0_n0_f32(float %x) {
; CHECK-LABEL: @test_class_is_p0_n0_f32(
-; CHECK-NEXT: [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 96)
+; CHECK-NEXT: [[VAL:%.*]] = fcmp oeq float [[X:%.*]], 0.000000e+00
; CHECK-NEXT: ret i1 [[VAL]]
;
%val = call i1 @llvm.is.fpclass.f32(float %x, i32 96) ; fcZero
define <2 x i1> @test_class_is_p0_n0_v2f32(<2 x float> %x) {
; CHECK-LABEL: @test_class_is_p0_n0_v2f32(
-; CHECK-NEXT: [[VAL:%.*]] = call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> [[X:%.*]], i32 96)
+; CHECK-NEXT: [[VAL:%.*]] = fcmp oeq <2 x float> [[X:%.*]], zeroinitializer
; CHECK-NEXT: ret <2 x i1> [[VAL]]
;
%val = call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> %x, i32 96) ; fcZero
define i1 @test_class_is_not_p0_n0_f32(float %x) {
; CHECK-LABEL: @test_class_is_not_p0_n0_f32(
-; CHECK-NEXT: [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 927)
+; CHECK-NEXT: [[VAL:%.*]] = fcmp une float [[X:%.*]], 0.000000e+00
; CHECK-NEXT: ret i1 [[VAL]]
;
%val = call i1 @llvm.is.fpclass.f32(float %x, i32 927) ; ~fcZero & fcAllFlags
define <2 x i1> @test_class_is_not_p0_n0_v2f32(<2 x float> %x) {
; CHECK-LABEL: @test_class_is_not_p0_n0_v2f32(
-; CHECK-NEXT: [[VAL:%.*]] = call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> [[X:%.*]], i32 927)
+; CHECK-NEXT: [[VAL:%.*]] = fcmp une <2 x float> [[X:%.*]], zeroinitializer
; CHECK-NEXT: ret <2 x i1> [[VAL]]
;
%val = call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> %x, i32 927) ; ~fcZero & fcAllFlags
define i1 @test_class_is_not_p0_n0_psub_nsub_f32_daz(float %x) "denormal-fp-math"="ieee,preserve-sign" {
; CHECK-LABEL: @test_class_is_not_p0_n0_psub_nsub_f32_daz(
-; CHECK-NEXT: [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 783)
+; CHECK-NEXT: [[VAL:%.*]] = fcmp une float [[X:%.*]], 0.000000e+00
; CHECK-NEXT: ret i1 [[VAL]]
;
%val = call i1 @llvm.is.fpclass.f32(float %x, i32 783) ; ~(fcZero|fcSubnormal) & fcAllFlags
define i1 @test_class_is_not_p0_n0_psub_nsub_f32_dapz(float %x) "denormal-fp-math"="ieee,positive-zero" {
; CHECK-LABEL: @test_class_is_not_p0_n0_psub_nsub_f32_dapz(
-; CHECK-NEXT: [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 783)
+; CHECK-NEXT: [[VAL:%.*]] = fcmp une float [[X:%.*]], 0.000000e+00
; CHECK-NEXT: ret i1 [[VAL]]
;
%val = call i1 @llvm.is.fpclass.f32(float %x, i32 783) ; ~(fcZero|fcSubnormal) & fcAllFlags
define i1 @test_class_is_p0_n0_psub_nsub_f32_daz(float %x) "denormal-fp-math"="ieee,preserve-sign" {
; CHECK-LABEL: @test_class_is_p0_n0_psub_nsub_f32_daz(
-; CHECK-NEXT: [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 240)
+; CHECK-NEXT: [[VAL:%.*]] = fcmp oeq float [[X:%.*]], 0.000000e+00
; CHECK-NEXT: ret i1 [[VAL]]
;
%val = call i1 @llvm.is.fpclass.f32(float %x, i32 240) ; fcZero | fcSubnormal
define i1 @test_class_is_p0_n0_psub_nsub_f32_dapz(float %x) "denormal-fp-math"="ieee,positive-zero" {
; CHECK-LABEL: @test_class_is_p0_n0_psub_nsub_f32_dapz(
-; CHECK-NEXT: [[VAL:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X:%.*]], i32 240)
+; CHECK-NEXT: [[VAL:%.*]] = fcmp oeq float [[X:%.*]], 0.000000e+00
; CHECK-NEXT: ret i1 [[VAL]]
;
%val = call i1 @llvm.is.fpclass.f32(float %x, i32 240) ; fcZero | fcSubnormal
define <2 x i1> @test_class_is_p0_n0_psub_nsub_v2f32_daz(<2 x float> %x) "denormal-fp-math"="ieee,preserve-sign" {
; CHECK-LABEL: @test_class_is_p0_n0_psub_nsub_v2f32_daz(
-; CHECK-NEXT: [[VAL:%.*]] = call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> [[X:%.*]], i32 240)
+; CHECK-NEXT: [[VAL:%.*]] = fcmp oeq <2 x float> [[X:%.*]], zeroinitializer
; CHECK-NEXT: ret <2 x i1> [[VAL]]
;
%val = call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> %x, i32 240) ; fcZero | fcSubnormal
define <2 x i1> @test_class_is_p0_n0_psub_nsub_v2f32_dapz(<2 x float> %x) "denormal-fp-math"="ieee,positive-zero" {
; CHECK-LABEL: @test_class_is_p0_n0_psub_nsub_v2f32_dapz(
-; CHECK-NEXT: [[VAL:%.*]] = call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> [[X:%.*]], i32 240)
+; CHECK-NEXT: [[VAL:%.*]] = fcmp oeq <2 x float> [[X:%.*]], zeroinitializer
; CHECK-NEXT: ret <2 x i1> [[VAL]]
;
%val = call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> %x, i32 240) ; fcZero | fcSubnormal
define i1 @test_class_fneg_zero(float %arg) {
;
; CHECK-LABEL: @test_class_fneg_zero(
-; CHECK-NEXT: [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[ARG:%.*]], i32 96)
+; CHECK-NEXT: [[CLASS:%.*]] = fcmp oeq float [[ARG:%.*]], 0.000000e+00
; CHECK-NEXT: ret i1 [[CLASS]]
;
%fneg = fneg float %arg
; -> poszero
define i1 @test_class_fabs_poszero(float %arg) {
; CHECK-LABEL: @test_class_fabs_poszero(
-; CHECK-NEXT: [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[ARG:%.*]], i32 96)
+; CHECK-NEXT: [[CLASS:%.*]] = fcmp oeq float [[ARG:%.*]], 0.000000e+00
; CHECK-NEXT: ret i1 [[CLASS]]
;
%fabs = call float @llvm.fabs.f32(float %arg)
; -> poszero
define i1 @test_class_fabs_zero(float %arg) {
; CHECK-LABEL: @test_class_fabs_zero(
-; CHECK-NEXT: [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[ARG:%.*]], i32 96)
+; CHECK-NEXT: [[CLASS:%.*]] = fcmp oeq float [[ARG:%.*]], 0.000000e+00
; CHECK-NEXT: ret i1 [[CLASS]]
;
%fabs = call float @llvm.fabs.f32(float %arg)
define i1 @test_class_fneg_fabs_negzero(float %arg) {
; CHECK-LABEL: @test_class_fneg_fabs_negzero(
-; CHECK-NEXT: [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[ARG:%.*]], i32 96)
+; CHECK-NEXT: [[CLASS:%.*]] = fcmp oeq float [[ARG:%.*]], 0.000000e+00
; CHECK-NEXT: ret i1 [[CLASS]]
;
%fabs = call float @llvm.fabs.f32(float %arg)
define i1 @test_class_fneg_fabs_zero(float %arg) {
; CHECK-LABEL: @test_class_fneg_fabs_zero(
-; CHECK-NEXT: [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f32(float [[ARG:%.*]], i32 96)
+; CHECK-NEXT: [[CLASS:%.*]] = fcmp oeq float [[ARG:%.*]], 0.000000e+00
; CHECK-NEXT: ret i1 [[CLASS]]
;
%fabs = call float @llvm.fabs.f32(float %arg)