diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index d295853798b80..15ec3fd2ceca0 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -1827,6 +1827,33 @@ Instruction *InstCombinerImpl::foldICmpAndConstConst(ICmpInst &Cmp, } } + // (icmp eq (and (bitcast X to int), ExponentMask), ExponentMask) --> + // llvm.is.fpclass(X, fcInf|fcNan) + // (icmp ne (and (bitcast X to int), ExponentMask), ExponentMask) --> + // llvm.is.fpclass(X, ~(fcInf|fcNan)) + Value *V; + if (!Cmp.getParent()->getParent()->hasFnAttribute( + Attribute::NoImplicitFloat) && + Cmp.isEquality() && match(X, m_OneUse(m_BitCast(m_Value(V))))) { + Type *SrcType = V->getType(); + Type *DstType = X->getType(); + Type *FPType = SrcType->getScalarType(); + // Make sure the bitcast doesn't change between scalar and vector and + // doesn't change the number of vector elements. + if (SrcType->isVectorTy() == DstType->isVectorTy() && + SrcType->getScalarSizeInBits() == DstType->getScalarSizeInBits() && + FPType->isIEEELikeFPTy() && C1 == *C2) { + APInt ExponentMask = + APFloat::getInf(FPType->getFltSemantics()).bitcastToAPInt(); + if (C1 == ExponentMask) { + unsigned Mask = FPClassTest::fcNan | FPClassTest::fcInf; + if (isICMP_NE) + Mask = ~Mask & fcAllFlags; + return replaceInstUsesWith(Cmp, Builder.createIsFPClass(V, Mask)); + } + } + } + return nullptr; } diff --git a/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll b/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll new file mode 100644 index 0000000000000..019db343c61cc --- /dev/null +++ b/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll @@ -0,0 +1,203 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4 +; RUN: opt -S -passes=instcombine < %s | FileCheck %s + +define i1 @f32_fcnan_fcinf(float %a) { +; CHECK-LABEL: define i1 @f32_fcnan_fcinf( +; CHECK-SAME: float [[A:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[A]]) +; CHECK-NEXT: [[CMP:%.*]] = fcmp ueq float [[TMP1]], 0x7FF0000000000000 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i32 = bitcast float %a to i32 + %and = and i32 %i32, 2139095040 + %cmp = icmp eq i32 %and, 2139095040 + ret i1 %cmp +} + +define i1 @f32_not_fcnan_fcinf(float %a) { +; CHECK-LABEL: define i1 @f32_not_fcnan_fcinf( +; CHECK-SAME: float [[A:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[A]]) +; CHECK-NEXT: [[CMP:%.*]] = fcmp one float [[TMP1]], 0x7FF0000000000000 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i32 = bitcast float %a to i32 + %and = and i32 %i32, 2139095040 + %cmp = icmp ne i32 %and, 2139095040 + ret i1 %cmp +} + +define i1 @f64_fcnan_fcinf(double %a) { +; CHECK-LABEL: define i1 @f64_fcnan_fcinf( +; CHECK-SAME: double [[A:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.fabs.f64(double [[A]]) +; CHECK-NEXT: [[CMP:%.*]] = fcmp ueq double [[TMP1]], 0x7FF0000000000000 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i64 = bitcast double %a to i64 + %and = and i64 %i64, 9218868437227405312 + %cmp = icmp eq i64 %and, 9218868437227405312 + ret i1 %cmp +} + +; TODO: handle more fpclass check idioms +define i1 @f32_fcinf(float %a) { +; CHECK-LABEL: define i1 @f32_fcinf( +; CHECK-SAME: float [[A:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[A]]) +; CHECK-NEXT: [[AND:%.*]] = bitcast float [[TMP1]] to i32 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[AND]], 2139095040 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i32 = bitcast float %a to i32 + %and = and i32 %i32, 2147483647 + %cmp = icmp eq i32 %and, 2139095040 + ret i1 %cmp +} + +define i1 @f32_fcnan(float %a) { +; CHECK-LABEL: define i1 @f32_fcnan( +; CHECK-SAME: float [[A:%.*]]) { +; CHECK-NEXT: [[I32:%.*]] = bitcast float [[A]] to i32 +; CHECK-NEXT: [[AND1:%.*]] = and i32 [[I32]], 2139095040 +; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[AND1]], 2139095040 +; CHECK-NEXT: [[AND2:%.*]] = and i32 [[I32]], 8388607 +; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i32 [[AND2]], 0 +; CHECK-NEXT: [[RES:%.*]] = and i1 [[CMP1]], [[CMP2]] +; CHECK-NEXT: ret i1 [[RES]] +; + %i32 = bitcast float %a to i32 + %and1 = and i32 %i32, 2139095040 + %cmp1 = icmp eq i32 %and1, 2139095040 + %and2 = and i32 %i32, 8388607 + %cmp2 = icmp ne i32 %and2, 0 + %res = and i1 %cmp1, %cmp2 + ret i1 %res +} + +define i1 @f32_fcnan_fcinf_strictfp(float %a) strictfp { +; CHECK-LABEL: define i1 @f32_fcnan_fcinf_strictfp( +; CHECK-SAME: float [[A:%.*]]) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[A]]) +; CHECK-NEXT: [[CMP:%.*]] = fcmp ueq float [[TMP1]], 0x7FF0000000000000 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i32 = bitcast float %a to i32 + %and = and i32 %i32, 2139095040 + %cmp = icmp eq i32 %and, 2139095040 + ret i1 %cmp +} + +define <2 x i1> @f32_fcnan_fcinf_vec(<2 x float> %a) { +; CHECK-LABEL: define <2 x i1> @f32_fcnan_fcinf_vec( +; CHECK-SAME: <2 x float> [[A:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = call <2 x float> @llvm.fabs.v2f32(<2 x float> [[A]]) +; CHECK-NEXT: [[CMP:%.*]] = fcmp ueq <2 x float> [[TMP1]], +; CHECK-NEXT: ret <2 x i1> [[CMP]] +; + %i32 = bitcast <2 x float> %a to <2 x i32> + %and = and <2 x i32> %i32, + %cmp = icmp eq <2 x i32> %and, + ret <2 x i1> %cmp +} + +; Negative tests + +define i1 @f32_fcnan_fcinf_wrong_mask1(float %a) { +; CHECK-LABEL: define i1 @f32_fcnan_fcinf_wrong_mask1( +; CHECK-SAME: float [[A:%.*]]) { +; CHECK-NEXT: [[I32:%.*]] = bitcast float [[A]] to i32 +; CHECK-NEXT: [[AND:%.*]] = and i32 [[I32]], 2139095041 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[AND]], 2139095040 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i32 = bitcast float %a to i32 + %and = and i32 %i32, 2139095041 + %cmp = icmp eq i32 %and, 2139095040 + ret i1 %cmp +} + +define i1 @f32_fcnan_fcinf_wrong_mask2(float %a) { +; CHECK-LABEL: define i1 @f32_fcnan_fcinf_wrong_mask2( +; CHECK-SAME: float [[A:%.*]]) { +; CHECK-NEXT: [[I32:%.*]] = bitcast float [[A]] to i32 +; CHECK-NEXT: [[AND:%.*]] = and i32 [[I32]], 2139095040 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[AND]], 2130706432 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i32 = bitcast float %a to i32 + %and = and i32 %i32, 2139095040 + %cmp = icmp eq i32 %and, 2130706432 + ret i1 %cmp +} + +define i1 @f64_fcnan_fcinf_wrong_mask3(double %a) { +; CHECK-LABEL: define i1 @f64_fcnan_fcinf_wrong_mask3( +; CHECK-SAME: double [[A:%.*]]) { +; CHECK-NEXT: [[I64:%.*]] = bitcast double [[A]] to i64 +; CHECK-NEXT: [[AND:%.*]] = and i64 [[I64]], 2139095040 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[AND]], 2139095040 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i64 = bitcast double %a to i64 + %and = and i64 %i64, 2139095040 + %cmp = icmp eq i64 %and, 2139095040 + ret i1 %cmp +} + +define i1 @f32_fcnan_fcinf_wrong_pred(float %a) { +; CHECK-LABEL: define i1 @f32_fcnan_fcinf_wrong_pred( +; CHECK-SAME: float [[A:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[A]]) +; CHECK-NEXT: [[CMP:%.*]] = fcmp one float [[TMP1]], 0x7FF0000000000000 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i32 = bitcast float %a to i32 + %and = and i32 %i32, 2139095040 + %cmp = icmp slt i32 %and, 2139095040 + ret i1 %cmp +} + +define i1 @f32_fcnan_fcinf_wrong_type1(<2 x float> %a) { +; CHECK-LABEL: define i1 @f32_fcnan_fcinf_wrong_type1( +; CHECK-SAME: <2 x float> [[A:%.*]]) { +; CHECK-NEXT: [[I64:%.*]] = bitcast <2 x float> [[A]] to i64 +; CHECK-NEXT: [[AND:%.*]] = and i64 [[I64]], 2139095040 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[AND]], 2139095040 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i64 = bitcast <2 x float> %a to i64 + %and = and i64 %i64, 2139095040 + %cmp = icmp eq i64 %and, 2139095040 + ret i1 %cmp +} + +define i1 @f32_fcnan_fcinf_wrong_type2(x86_fp80 %a) { +; CHECK-LABEL: define i1 @f32_fcnan_fcinf_wrong_type2( +; CHECK-SAME: x86_fp80 [[A:%.*]]) { +; CHECK-NEXT: [[I80:%.*]] = bitcast x86_fp80 [[A]] to i80 +; CHECK-NEXT: [[AND:%.*]] = and i80 [[I80]], 2139095040 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i80 [[AND]], 2139095040 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i80 = bitcast x86_fp80 %a to i80 + %and = and i80 %i80, 2139095040 + %cmp = icmp eq i80 %and, 2139095040 + ret i1 %cmp +} + +define i1 @f32_fcnan_fcinf_noimplicitfloat(float %a) #0 { +; CHECK-LABEL: define i1 @f32_fcnan_fcinf_noimplicitfloat( +; CHECK-SAME: float [[A:%.*]]) #[[ATTR1:[0-9]+]] { +; CHECK-NEXT: [[I32:%.*]] = bitcast float [[A]] to i32 +; CHECK-NEXT: [[AND:%.*]] = and i32 [[I32]], 2139095040 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[AND]], 2139095040 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i32 = bitcast float %a to i32 + %and = and i32 %i32, 2139095040 + %cmp = icmp eq i32 %and, 2139095040 + ret i1 %cmp +} + +attributes #0 = { noimplicitfloat }