diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index 7aac13fd474b2..cbb69887642ba 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -3229,16 +3229,16 @@ Instruction *InstCombinerImpl::foldICmpBitCast(ICmpInst &Cmp) { if (Cmp.isEquality() && match(Op1, m_Zero())) return new ICmpInst(Pred, X, ConstantInt::getNullValue(X->getType())); - // If this is a sign-bit test of a bitcast of a casted FP value, eliminate - // the FP extend/truncate because that cast does not change the sign-bit. - // This is true for all standard IEEE-754 types and the X86 80-bit type. - // The sign-bit is always the most significant bit in those types. const APInt *C; bool TrueIfSigned; - if (match(Op1, m_APInt(C)) && Bitcast->hasOneUse() && - isSignBitCheck(Pred, *C, TrueIfSigned)) { - if (match(BCSrcOp, m_FPExt(m_Value(X))) || - match(BCSrcOp, m_FPTrunc(m_Value(X)))) { + if (match(Op1, m_APInt(C)) && Bitcast->hasOneUse()) { + // If this is a sign-bit test of a bitcast of a casted FP value, eliminate + // the FP extend/truncate because that cast does not change the sign-bit. + // This is true for all standard IEEE-754 types and the X86 80-bit type. + // The sign-bit is always the most significant bit in those types. + if (isSignBitCheck(Pred, *C, TrueIfSigned) && + (match(BCSrcOp, m_FPExt(m_Value(X))) || + match(BCSrcOp, m_FPTrunc(m_Value(X))))) { // (bitcast (fpext/fptrunc X)) to iX) < 0 --> (bitcast X to iY) < 0 // (bitcast (fpext/fptrunc X)) to iX) > -1 --> (bitcast X to iY) > -1 Type *XType = X->getType(); @@ -3257,6 +3257,20 @@ Instruction *InstCombinerImpl::foldICmpBitCast(ICmpInst &Cmp) { ConstantInt::getAllOnesValue(NewType)); } } + + // icmp eq/ne (bitcast X to int), special fp -> llvm.is.fpclass(X, class) + Type *FPType = SrcType->getScalarType(); + if (!Cmp.getParent()->getParent()->hasFnAttribute( + Attribute::NoImplicitFloat) && + Cmp.isEquality() && FPType->isIEEELikeFPTy()) { + FPClassTest Mask = APFloat(FPType->getFltSemantics(), *C).classify(); + if (Mask & (fcInf | fcZero)) { + if (Pred == ICmpInst::ICMP_NE) + Mask = ~Mask; + return replaceInstUsesWith(Cmp, + Builder.createIsFPClass(BCSrcOp, Mask)); + } + } } } diff --git a/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll b/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll index 019db343c61cc..d2b4536448ebd 100644 --- a/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll +++ b/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll @@ -40,13 +40,11 @@ define i1 @f64_fcnan_fcinf(double %a) { 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: [[CMP:%.*]] = fcmp oeq float [[TMP1]], 0x7FF0000000000000 ; CHECK-NEXT: ret i1 [[CMP]] ; %i32 = bitcast float %a to i32 @@ -55,6 +53,63 @@ define i1 @f32_fcinf(float %a) { ret i1 %cmp } +define i1 @f32_fcposinf(float %a) { +; CHECK-LABEL: define i1 @f32_fcposinf( +; CHECK-SAME: float [[A:%.*]]) { +; CHECK-NEXT: [[CMP:%.*]] = fcmp oeq float [[A]], 0x7FF0000000000000 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i32 = bitcast float %a to i32 + %cmp = icmp eq i32 %i32, 2139095040 + ret i1 %cmp +} + +define i1 @f32_fcneginf(float %a) { +; CHECK-LABEL: define i1 @f32_fcneginf( +; CHECK-SAME: float [[A:%.*]]) { +; CHECK-NEXT: [[CMP:%.*]] = fcmp oeq float [[A]], 0xFFF0000000000000 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i32 = bitcast float %a to i32 + %cmp = icmp eq i32 %i32, 4286578688 + ret i1 %cmp +} + +define i1 @f32_fcposzero(float %a) { +; CHECK-LABEL: define i1 @f32_fcposzero( +; CHECK-SAME: float [[A:%.*]]) { +; CHECK-NEXT: [[CMP:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 64) +; CHECK-NEXT: ret i1 [[CMP]] +; + %i32 = bitcast float %a to i32 + %cmp = icmp eq i32 %i32, 0 + ret i1 %cmp +} + +define i1 @f32_fcnegzero(float %a) { +; CHECK-LABEL: define i1 @f32_fcnegzero( +; CHECK-SAME: float [[A:%.*]]) { +; CHECK-NEXT: [[CMP:%.*]] = call i1 @llvm.is.fpclass.f32(float [[A]], i32 32) +; CHECK-NEXT: ret i1 [[CMP]] +; + %i32 = bitcast float %a to i32 + %cmp = icmp eq i32 %i32, 2147483648 + ret i1 %cmp +} + +define i1 @f32_fczero(float %a) { +; CHECK-LABEL: define i1 @f32_fczero( +; CHECK-SAME: float [[A:%.*]]) { +; CHECK-NEXT: [[CMP:%.*]] = fcmp oeq float [[A]], 0.000000e+00 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i32 = bitcast float %a to i32 + %and = and i32 %i32, 2147483647 + %cmp = icmp eq i32 %and, 0 + ret i1 %cmp +} + +; TODO: handle more fpclass check idioms define i1 @f32_fcnan(float %a) { ; CHECK-LABEL: define i1 @f32_fcnan( ; CHECK-SAME: float [[A:%.*]]) { @@ -101,6 +156,19 @@ define <2 x i1> @f32_fcnan_fcinf_vec(<2 x float> %a) { ret <2 x i1> %cmp } +define <2 x i1> @f32_fcinf_vec(<2 x float> %a) { +; CHECK-LABEL: define <2 x i1> @f32_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 oeq <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) { @@ -158,6 +226,18 @@ define i1 @f32_fcnan_fcinf_wrong_pred(float %a) { ret i1 %cmp } +define i1 @f32_fcposzero_wrong_pred(float %a) { +; CHECK-LABEL: define i1 @f32_fcposzero_wrong_pred( +; CHECK-SAME: float [[A:%.*]]) { +; CHECK-NEXT: [[I32:%.*]] = bitcast float [[A]] to i32 +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[I32]], 0 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i32 = bitcast float %a to i32 + %cmp = icmp slt i32 %i32, 0 + 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:%.*]]) { @@ -172,6 +252,18 @@ define i1 @f32_fcnan_fcinf_wrong_type1(<2 x float> %a) { ret i1 %cmp } +define i1 @f32_fcposinf_wrong_type1(<2 x float> %a) { +; CHECK-LABEL: define i1 @f32_fcposinf_wrong_type1( +; CHECK-SAME: <2 x float> [[A:%.*]]) { +; CHECK-NEXT: [[I64:%.*]] = bitcast <2 x float> [[A]] to i64 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[I64]], 2139095040 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i64 = bitcast <2 x float> %a to i64 + %cmp = icmp eq i64 %i64, 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:%.*]]) { @@ -186,6 +278,18 @@ define i1 @f32_fcnan_fcinf_wrong_type2(x86_fp80 %a) { ret i1 %cmp } +define i1 @f32_fcposzero_wrong_type2(x86_fp80 %a) { +; CHECK-LABEL: define i1 @f32_fcposzero_wrong_type2( +; CHECK-SAME: x86_fp80 [[A:%.*]]) { +; CHECK-NEXT: [[I80:%.*]] = bitcast x86_fp80 [[A]] to i80 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i80 [[I80]], 0 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i80 = bitcast x86_fp80 %a to i80 + %cmp = icmp eq i80 %i80, 0 + 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]+]] { @@ -200,4 +304,44 @@ define i1 @f32_fcnan_fcinf_noimplicitfloat(float %a) #0 { ret i1 %cmp } +define i1 @f32_fcposinf_noimplicitfloat(float %a) #0 { +; CHECK-LABEL: define i1 @f32_fcposinf_noimplicitfloat( +; CHECK-SAME: float [[A:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: [[I32:%.*]] = bitcast float [[A]] to i32 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[I32]], 2139095040 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i32 = bitcast float %a to i32 + %cmp = icmp eq i32 %i32, 2139095040 + ret i1 %cmp +} + +define i1 @f32_fcposnan(float %a) { +; CHECK-LABEL: define i1 @f32_fcposnan( +; CHECK-SAME: float [[A:%.*]]) { +; CHECK-NEXT: [[I32:%.*]] = bitcast float [[A]] to i32 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[I32]], 2139095041 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i32 = bitcast float %a to i32 + %cmp = icmp eq i32 %i32, 2139095041 + ret i1 %cmp +} + +define i1 @f32_fcposinf_multiuse(float %a) { +; CHECK-LABEL: define i1 @f32_fcposinf_multiuse( +; CHECK-SAME: float [[A:%.*]]) { +; CHECK-NEXT: [[I32:%.*]] = bitcast float [[A]] to i32 +; CHECK-NEXT: call void @usei32(i32 [[I32]]) +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[I32]], 2139095040 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i32 = bitcast float %a to i32 + call void @usei32(i32 %i32) + %cmp = icmp eq i32 %i32, 2139095040 + ret i1 %cmp +} + +declare void @usei32(i32) + attributes #0 = { noimplicitfloat }