[llvm] a10f6c1 - [InstCombine] Handle isnormal idiom (#125454)

via llvm-commits llvm-commits at lists.llvm.org
Tue May 6 08:55:08 PDT 2025


Author: Yingwei Zheng
Date: 2025-05-06T23:55:04+08:00
New Revision: a10f6c1e689aaa0fee86db740b99d3895076a61c

URL: https://github.com/llvm/llvm-project/commit/a10f6c1e689aaa0fee86db740b99d3895076a61c
DIFF: https://github.com/llvm/llvm-project/commit/a10f6c1e689aaa0fee86db740b99d3895076a61c.diff

LOG: [InstCombine] Handle isnormal idiom (#125454)

This patch improves the codegen of Rust's `is_normal` implementation:
https://godbolt.org/z/1MPzcrrYG
Alive2: https://alive2.llvm.org/ce/z/hF9RWQ

Added: 
    

Modified: 
    llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
    llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index f059d87e581fd..100c144c177fe 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -3522,6 +3522,23 @@ Value *InstCombinerImpl::foldAndOrOfICmps(ICmpInst *LHS, ICmpInst *RHS,
     }
   }
 
+  // (X & ExpMask) != 0 && (X & ExpMask) != ExpMask -> isnormal(X)
+  // (X & ExpMask) == 0 || (X & ExpMask) == ExpMask -> !isnormal(X)
+  Value *X;
+  const APInt *MaskC;
+  if (LHS0 == RHS0 && PredL == PredR &&
+      PredL == (IsAnd ? ICmpInst::ICMP_NE : ICmpInst::ICMP_EQ) &&
+      !I.getFunction()->hasFnAttribute(Attribute::NoImplicitFloat) &&
+      LHS->hasOneUse() && RHS->hasOneUse() &&
+      match(LHS0, m_And(m_ElementWiseBitCast(m_Value(X)), m_APInt(MaskC))) &&
+      X->getType()->getScalarType()->isIEEELikeFPTy() &&
+      APFloat(X->getType()->getScalarType()->getFltSemantics(), *MaskC)
+          .isPosInfinity() &&
+      ((LHSC->isZero() && *RHSC == *MaskC) ||
+       (RHSC->isZero() && *LHSC == *MaskC)))
+    return Builder.createIsFPClass(X, IsAnd ? FPClassTest::fcNormal
+                                            : ~FPClassTest::fcNormal);
+
   return foldAndOrOfICmpsUsingRanges(LHS, RHS, IsAnd);
 }
 

diff  --git a/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll b/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
index bb5713a1a56ea..4695749cd7be8 100644
--- a/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
+++ b/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
@@ -1013,6 +1013,215 @@ define i1 @isnan_idiom_ppc_fp128(ppc_fp128 %x) {
   ret i1 %ret
 }
 
+define i1 @fpclass_test_normal(float %num) {
+; CHECK-LABEL: define i1 @fpclass_test_normal(
+; CHECK-SAME: float [[NUM:%.*]]) {
+; CHECK-NEXT:    [[RES:%.*]] = call i1 @llvm.is.fpclass.f32(float [[NUM]], i32 264)
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %cast = bitcast float %num to i32
+  %masked = and i32 %cast, 2139095040
+  %test1 = icmp ne i32 %masked, 2139095040
+  %test2 = icmp ne i32 %masked, 0
+  %res = and i1 %test1, %test2
+  ret i1 %res
+}
+
+define i1 @fpclass_test_normal_half(half %num) {
+; CHECK-LABEL: define i1 @fpclass_test_normal_half(
+; CHECK-SAME: half [[NUM:%.*]]) {
+; CHECK-NEXT:    [[RES:%.*]] = call i1 @llvm.is.fpclass.f16(half [[NUM]], i32 264)
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %cast = bitcast half %num to i16
+  %masked = and i16 %cast, 31744
+  %test1 = icmp ne i16 %masked, 31744
+  %test2 = icmp ne i16 %masked, 0
+  %res = and i1 %test1, %test2
+  ret i1 %res
+}
+
+define <2 x i1> @fpclass_test_normal_half_vec(<2 x half> %num) {
+; CHECK-LABEL: define <2 x i1> @fpclass_test_normal_half_vec(
+; CHECK-SAME: <2 x half> [[NUM:%.*]]) {
+; CHECK-NEXT:    [[RES:%.*]] = call <2 x i1> @llvm.is.fpclass.v2f16(<2 x half> [[NUM]], i32 264)
+; CHECK-NEXT:    ret <2 x i1> [[RES]]
+;
+  %cast = bitcast <2 x half> %num to <2 x i16>
+  %masked = and <2 x i16> %cast, splat(i16 31744)
+  %test1 = icmp ne <2 x i16> %masked, splat(i16 31744)
+  %test2 = icmp ne <2 x i16> %masked, zeroinitializer
+  %res = and <2 x i1> %test1, %test2
+  ret <2 x i1> %res
+}
+
+define i1 @fpclass_test_not_normal(float %num) {
+; CHECK-LABEL: define i1 @fpclass_test_not_normal(
+; CHECK-SAME: float [[NUM:%.*]]) {
+; CHECK-NEXT:    [[RES:%.*]] = call i1 @llvm.is.fpclass.f32(float [[NUM]], i32 759)
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %cast = bitcast float %num to i32
+  %masked = and i32 %cast, 2139095040
+  %test1 = icmp eq i32 %masked, 2139095040
+  %test2 = icmp eq i32 %masked, 0
+  %res = or i1 %test1, %test2
+  ret i1 %res
+}
+
+define <2 x i1> @fpclass_test_not_normal_vec(<2 x float> %num) {
+; CHECK-LABEL: define <2 x i1> @fpclass_test_not_normal_vec(
+; CHECK-SAME: <2 x float> [[NUM:%.*]]) {
+; CHECK-NEXT:    [[RES:%.*]] = call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> [[NUM]], i32 759)
+; CHECK-NEXT:    ret <2 x i1> [[RES]]
+;
+  %cast = bitcast <2 x float> %num to <2 x i32>
+  %masked = and <2 x i32> %cast, splat(i32 2139095040)
+  %test1 = icmp eq <2 x i32> %masked, splat(i32 2139095040)
+  %test2 = icmp eq <2 x i32> %masked, zeroinitializer
+  %res = or <2 x i1> %test1, %test2
+  ret <2 x i1> %res
+}
+
+define i1 @fpclass_test_normal_commuted(float %num) {
+; CHECK-LABEL: define i1 @fpclass_test_normal_commuted(
+; CHECK-SAME: float [[NUM:%.*]]) {
+; CHECK-NEXT:    [[RES:%.*]] = call i1 @llvm.is.fpclass.f32(float [[NUM]], i32 264)
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %cast = bitcast float %num to i32
+  %masked = and i32 %cast, 2139095040
+  %test1 = icmp ne i32 %masked, 2139095040
+  %test2 = icmp ne i32 %masked, 0
+  %res = and i1 %test2, %test1
+  ret i1 %res
+}
+
+; Negative tests
+
+define i1 @fpclass_test_normal_fp128(ppc_fp128 %x) {
+; CHECK-LABEL: define i1 @fpclass_test_normal_fp128(
+; CHECK-SAME: ppc_fp128 [[X:%.*]]) {
+; CHECK-NEXT:    [[BITS:%.*]] = bitcast ppc_fp128 [[X]] to i128
+; CHECK-NEXT:    [[MASKED:%.*]] = and i128 [[BITS]], 170058106710732674489630815774616584192
+; CHECK-NEXT:    [[TEST1:%.*]] = icmp ne i128 [[MASKED]], 170058106710732674489630815774616584192
+; CHECK-NEXT:    [[TEST2:%.*]] = icmp ne i128 [[MASKED]], 0
+; CHECK-NEXT:    [[RES:%.*]] = and i1 [[TEST2]], [[TEST1]]
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %bits = bitcast ppc_fp128 %x to i128
+  %masked = and i128 %bits, 170058106710732674489630815774616584192
+  %test1 = icmp ne i128 %masked, 170058106710732674489630815774616584192
+  %test2 = icmp ne i128 %masked, 0
+  %res = and i1 %test2, %test1
+  ret i1 %res
+}
+
+define i1 @fpclass_test_normal_mismatch_pred(float %num) {
+; CHECK-LABEL: define i1 @fpclass_test_normal_mismatch_pred(
+; CHECK-SAME: float [[NUM:%.*]]) {
+; CHECK-NEXT:    [[TEST2:%.*]] = call i1 @llvm.is.fpclass.f32(float [[NUM]], i32 240)
+; CHECK-NEXT:    ret i1 [[TEST2]]
+;
+  %cast = bitcast float %num to i32
+  %masked = and i32 %cast, 2139095040
+  %test1 = icmp ne i32 %masked, 2139095040
+  %test2 = icmp eq i32 %masked, 0
+  %res = and i1 %test1, %test2
+  ret i1 %res
+}
+
+define i1 @fpclass_test_normal_no_implicit_fp(float %num) #0 {
+; CHECK-LABEL: define i1 @fpclass_test_normal_no_implicit_fp(
+; CHECK-SAME: float [[NUM:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast float [[NUM]] to i32
+; CHECK-NEXT:    [[MASKED:%.*]] = and i32 [[CAST]], 2139095040
+; CHECK-NEXT:    [[TEST1:%.*]] = icmp ne i32 [[MASKED]], 2139095040
+; CHECK-NEXT:    [[TEST2:%.*]] = icmp ne i32 [[MASKED]], 0
+; CHECK-NEXT:    [[RES:%.*]] = and i1 [[TEST1]], [[TEST2]]
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %cast = bitcast float %num to i32
+  %masked = and i32 %cast, 2139095040
+  %test1 = icmp ne i32 %masked, 2139095040
+  %test2 = icmp ne i32 %masked, 0
+  %res = and i1 %test1, %test2
+  ret i1 %res
+}
+
+define i1 @fpclass_test_normal_invalid_constant1(float %num) {
+; CHECK-LABEL: define i1 @fpclass_test_normal_invalid_constant1(
+; CHECK-SAME: float [[NUM:%.*]]) {
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast float [[NUM]] to i32
+; CHECK-NEXT:    [[MASKED:%.*]] = and i32 [[CAST]], 2139095039
+; CHECK-NEXT:    [[TEST1:%.*]] = icmp ne i32 [[MASKED]], 2139095039
+; CHECK-NEXT:    [[TEST2:%.*]] = icmp ne i32 [[MASKED]], 0
+; CHECK-NEXT:    [[RES:%.*]] = and i1 [[TEST1]], [[TEST2]]
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %cast = bitcast float %num to i32
+  %masked = and i32 %cast, 2139095039
+  %test1 = icmp ne i32 %masked, 2139095039
+  %test2 = icmp ne i32 %masked, 0
+  %res = and i1 %test1, %test2
+  ret i1 %res
+}
+
+define i1 @fpclass_test_normal_invalid_constant2(float %num) {
+; CHECK-LABEL: define i1 @fpclass_test_normal_invalid_constant2(
+; CHECK-SAME: float [[NUM:%.*]]) {
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast float [[NUM]] to i32
+; CHECK-NEXT:    [[MASKED:%.*]] = and i32 [[CAST]], 2139095040
+; CHECK-NEXT:    [[TEST1:%.*]] = icmp ne i32 [[MASKED]], 2130706432
+; CHECK-NEXT:    [[TEST2:%.*]] = icmp ne i32 [[MASKED]], 0
+; CHECK-NEXT:    [[RES:%.*]] = and i1 [[TEST1]], [[TEST2]]
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %cast = bitcast float %num to i32
+  %masked = and i32 %cast, 2139095040
+  %test1 = icmp ne i32 %masked, 2130706432
+  %test2 = icmp ne i32 %masked, 0
+  %res = and i1 %test1, %test2
+  ret i1 %res
+}
+
+define i1 @fpclass_test_normal_invalid_constant3(float %num) {
+; CHECK-LABEL: define i1 @fpclass_test_normal_invalid_constant3(
+; CHECK-SAME: float [[NUM:%.*]]) {
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast float [[NUM]] to i32
+; CHECK-NEXT:    [[TMP1:%.*]] = and i32 [[CAST]], 2130706432
+; CHECK-NEXT:    [[RES:%.*]] = icmp ne i32 [[TMP1]], 2130706432
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %cast = bitcast float %num to i32
+  %masked = and i32 %cast, 2139095040
+  %test1 = icmp ne i32 %masked, 2139095040
+  %test2 = icmp ne i32 %masked, 2130706432
+  %res = and i1 %test1, %test2
+  ret i1 %res
+}
+
+define i1 @fpclass_test_normal_multiuse(float %num) {
+; CHECK-LABEL: define i1 @fpclass_test_normal_multiuse(
+; CHECK-SAME: float [[NUM:%.*]]) {
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast float [[NUM]] to i32
+; CHECK-NEXT:    [[MASKED:%.*]] = and i32 [[CAST]], 2139095040
+; CHECK-NEXT:    [[TEST1:%.*]] = icmp ne i32 [[MASKED]], 2139095040
+; CHECK-NEXT:    [[TEST2:%.*]] = icmp ne i32 [[MASKED]], 0
+; CHECK-NEXT:    call void @usei1(i1 [[TEST1]])
+; CHECK-NEXT:    [[RES:%.*]] = and i1 [[TEST1]], [[TEST2]]
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %cast = bitcast float %num to i32
+  %masked = and i32 %cast, 2139095040
+  %test1 = icmp ne i32 %masked, 2139095040
+  %test2 = icmp ne i32 %masked, 0
+  call void @usei1(i1 %test1)
+  %res = and i1 %test1, %test2
+  ret i1 %res
+}
+
 declare void @usei32(i32)
+declare void @usei1(i1)
 
 attributes #0 = { noimplicitfloat }


        


More information about the llvm-commits mailing list