[llvm] dba3dfa - [InstCombine] Fold isnan idioms (#101510)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 2 06:21:34 PDT 2024
Author: Yingwei Zheng
Date: 2024-08-02T21:21:31+08:00
New Revision: dba3dfa2bdbf8924bf16efcf896664fb91d38295
URL: https://github.com/llvm/llvm-project/commit/dba3dfa2bdbf8924bf16efcf896664fb91d38295
DIFF: https://github.com/llvm/llvm-project/commit/dba3dfa2bdbf8924bf16efcf896664fb91d38295.diff
LOG: [InstCombine] Fold isnan idioms (#101510)
Folds `(icmp ne ((bitcast X) & FractionBits), 0) & (icmp eq ((bitcast X)
& ExpBits), ExpBits) -> fcmp uno X, 0.0` and
`(icmp eq ((bitcast X) & FractionBits), 0) | (icmp ne ((bitcast X) &
ExpBits), ExpBits) -> fcmp ord X, 0.0`
Alive2: https://alive2.llvm.org/ce/z/mrLn_x
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 4ca12d5b92f18..2db05c669145b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -326,8 +326,8 @@ static std::optional<std::pair<unsigned, unsigned>> getMaskedTypeForICmpPair(
/// (icmp (A & 12) != 0) & (icmp (A & 15) == 8) -> (icmp (A & 15) == 8).
/// Also used for logical and/or, must be poison safe.
static Value *foldLogOpOfMaskedICmps_NotAllZeros_BMask_Mixed(
- ICmpInst *LHS, ICmpInst *RHS, bool IsAnd, Value *A, Value *B, Value *C,
- Value *D, Value *E, ICmpInst::Predicate PredL, ICmpInst::Predicate PredR,
+ ICmpInst *LHS, ICmpInst *RHS, bool IsAnd, Value *A, Value *B, Value *D,
+ Value *E, ICmpInst::Predicate PredL, ICmpInst::Predicate PredR,
InstCombiner::BuilderTy &Builder) {
// We are given the canonical form:
// (icmp ne (A & B), 0) & (icmp eq (A & D), E).
@@ -339,9 +339,9 @@ static Value *foldLogOpOfMaskedICmps_NotAllZeros_BMask_Mixed(
//
// We currently handle the case of B, C, D, E are constant.
//
- const APInt *BCst, *CCst, *DCst, *OrigECst;
- if (!match(B, m_APInt(BCst)) || !match(C, m_APInt(CCst)) ||
- !match(D, m_APInt(DCst)) || !match(E, m_APInt(OrigECst)))
+ const APInt *BCst, *DCst, *OrigECst;
+ if (!match(B, m_APInt(BCst)) || !match(D, m_APInt(DCst)) ||
+ !match(E, m_APInt(OrigECst)))
return nullptr;
ICmpInst::Predicate NewCC = IsAnd ? ICmpInst::ICMP_EQ : ICmpInst::ICMP_NE;
@@ -359,12 +359,32 @@ static Value *foldLogOpOfMaskedICmps_NotAllZeros_BMask_Mixed(
if (*BCst == 0 || *DCst == 0)
return nullptr;
- // If B and D don't intersect, ie. (B & D) == 0, no folding because we can't
- // deduce anything from it.
- // For example,
- // (icmp ne (A & 12), 0) & (icmp eq (A & 3), 1) -> no folding.
- if ((*BCst & *DCst) == 0)
+ // If B and D don't intersect, ie. (B & D) == 0, try to fold isNaN idiom:
+ // (icmp ne (A & FractionBits), 0) & (icmp eq (A & ExpBits), ExpBits)
+ // -> isNaN(A)
+ // Otherwise, we cannot deduce anything from it.
+ if (!BCst->intersects(*DCst)) {
+ Value *Src;
+ if (*DCst == ECst && match(A, m_ElementWiseBitCast(m_Value(Src))) &&
+ !Builder.GetInsertBlock()->getParent()->hasFnAttribute(
+ Attribute::StrictFP)) {
+ Type *Ty = Src->getType()->getScalarType();
+ if (!Ty->isIEEELikeFPTy())
+ return nullptr;
+
+ APInt ExpBits = APFloat::getInf(Ty->getFltSemantics()).bitcastToAPInt();
+ if (ECst != ExpBits)
+ return nullptr;
+ APInt FractionBits = ~ExpBits;
+ FractionBits.clearSignBit();
+ if (*BCst != FractionBits)
+ return nullptr;
+
+ return Builder.CreateFCmp(IsAnd ? FCmpInst::FCMP_UNO : FCmpInst::FCMP_ORD,
+ Src, ConstantFP::getZero(Src->getType()));
+ }
return nullptr;
+ }
// If the following two conditions are met:
//
@@ -464,14 +484,12 @@ static Value *foldLogOpOfMaskedICmpsAsymmetric(
}
if ((LHSMask & Mask_NotAllZeros) && (RHSMask & BMask_Mixed)) {
if (Value *V = foldLogOpOfMaskedICmps_NotAllZeros_BMask_Mixed(
- LHS, RHS, IsAnd, A, B, C, D, E,
- PredL, PredR, Builder)) {
+ LHS, RHS, IsAnd, A, B, D, E, PredL, PredR, Builder)) {
return V;
}
} else if ((LHSMask & BMask_Mixed) && (RHSMask & Mask_NotAllZeros)) {
if (Value *V = foldLogOpOfMaskedICmps_NotAllZeros_BMask_Mixed(
- RHS, LHS, IsAnd, A, D, E, B, C,
- PredR, PredL, Builder)) {
+ RHS, LHS, IsAnd, A, D, B, C, PredR, PredL, Builder)) {
return V;
}
}
diff --git a/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll b/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
index 42c6506e34cf4..4034759df0604 100644
--- a/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
+++ b/llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll
@@ -217,12 +217,7 @@ define i1 @f32_fczero_strictfp(float %a) strictfp {
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: [[RES:%.*]] = fcmp uno float [[A]], 0.000000e+00
; CHECK-NEXT: ret i1 [[RES]]
;
%i32 = bitcast float %a to i32
@@ -647,6 +642,241 @@ define i1 @f32_fcposinf_multiuse_strictfp(float %a) strictfp {
ret i1 %cmp
}
+define i1 @isnan_idiom(double %x) {
+; CHECK-LABEL: define i1 @isnan_idiom(
+; CHECK-SAME: double [[X:%.*]]) {
+; CHECK-NEXT: [[RET:%.*]] = fcmp uno double [[X]], 0.000000e+00
+; CHECK-NEXT: ret i1 [[RET]]
+;
+ %bits = bitcast double %x to i64
+ %mask1 = and i64 %bits, 9218868437227405312
+ %cond1 = icmp eq i64 %mask1, 9218868437227405312
+ %mask2 = and i64 %bits, 4503599627370495
+ %cond2 = icmp ne i64 %mask2, 0
+ %ret = and i1 %cond1, %cond2
+ ret i1 %ret
+}
+
+define <2 x i1> @isnan_idiom_vec(<2 x double> %x) {
+; CHECK-LABEL: define <2 x i1> @isnan_idiom_vec(
+; CHECK-SAME: <2 x double> [[X:%.*]]) {
+; CHECK-NEXT: [[RET:%.*]] = fcmp uno <2 x double> [[X]], zeroinitializer
+; CHECK-NEXT: ret <2 x i1> [[RET]]
+;
+ %bits = bitcast <2 x double> %x to <2 x i64>
+ %mask1 = and <2 x i64> %bits, splat(i64 9218868437227405312)
+ %cond1 = icmp eq <2 x i64> %mask1, splat(i64 9218868437227405312)
+ %mask2 = and <2 x i64> %bits, splat(i64 4503599627370495)
+ %cond2 = icmp ne <2 x i64> %mask2, zeroinitializer
+ %ret = and <2 x i1> %cond1, %cond2
+ ret <2 x i1> %ret
+}
+
+define i1 @isnan_idiom_commuted(double %x) {
+; CHECK-LABEL: define i1 @isnan_idiom_commuted(
+; CHECK-SAME: double [[X:%.*]]) {
+; CHECK-NEXT: [[RET:%.*]] = fcmp uno double [[X]], 0.000000e+00
+; CHECK-NEXT: ret i1 [[RET]]
+;
+ %bits = bitcast double %x to i64
+ %mask1 = and i64 %bits, 9218868437227405312
+ %cond1 = icmp eq i64 %mask1, 9218868437227405312
+ %mask2 = and i64 %bits, 4503599627370495
+ %cond2 = icmp ne i64 %mask2, 0
+ %ret = and i1 %cond2, %cond1
+ ret i1 %ret
+}
+
+define i1 @isnotnan_idiom(double %x) {
+; CHECK-LABEL: define i1 @isnotnan_idiom(
+; CHECK-SAME: double [[X:%.*]]) {
+; CHECK-NEXT: [[RET:%.*]] = fcmp ord double [[X]], 0.000000e+00
+; CHECK-NEXT: ret i1 [[RET]]
+;
+ %bits = bitcast double %x to i64
+ %mask1 = and i64 %bits, 9218868437227405312
+ %cond1 = icmp ne i64 %mask1, 9218868437227405312
+ %mask2 = and i64 %bits, 4503599627370495
+ %cond2 = icmp eq i64 %mask2, 0
+ %ret = or i1 %cond1, %cond2
+ ret i1 %ret
+}
+
+; negative tests
+
+define i1 @isnan_idiom_strictfp(double %x) strictfp {
+; CHECK-LABEL: define i1 @isnan_idiom_strictfp(
+; CHECK-SAME: double [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[BITS:%.*]] = bitcast double [[X]] to i64
+; CHECK-NEXT: [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
+; CHECK-NEXT: [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405312
+; CHECK-NEXT: [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370495
+; CHECK-NEXT: [[COND2:%.*]] = icmp ne i64 [[MASK2]], 0
+; CHECK-NEXT: [[RET:%.*]] = and i1 [[COND1]], [[COND2]]
+; CHECK-NEXT: ret i1 [[RET]]
+;
+ %bits = bitcast double %x to i64
+ %mask1 = and i64 %bits, 9218868437227405312
+ %cond1 = icmp eq i64 %mask1, 9218868437227405312
+ %mask2 = and i64 %bits, 4503599627370495
+ %cond2 = icmp ne i64 %mask2, 0
+ %ret = and i1 %cond1, %cond2
+ ret i1 %ret
+}
+
+define i1 @isnan_idiom_wrong_pred1(double %x) {
+; CHECK-LABEL: define i1 @isnan_idiom_wrong_pred1(
+; CHECK-SAME: double [[X:%.*]]) {
+; CHECK-NEXT: [[BITS:%.*]] = bitcast double [[X]] to i64
+; CHECK-NEXT: [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
+; CHECK-NEXT: [[COND1:%.*]] = icmp ne i64 [[MASK1]], 9218868437227405312
+; CHECK-NEXT: [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370495
+; CHECK-NEXT: [[COND2:%.*]] = icmp ne i64 [[MASK2]], 0
+; CHECK-NEXT: [[RET:%.*]] = and i1 [[COND1]], [[COND2]]
+; CHECK-NEXT: ret i1 [[RET]]
+;
+ %bits = bitcast double %x to i64
+ %mask1 = and i64 %bits, 9218868437227405312
+ %cond1 = icmp ne i64 %mask1, 9218868437227405312
+ %mask2 = and i64 %bits, 4503599627370495
+ %cond2 = icmp ne i64 %mask2, 0
+ %ret = and i1 %cond1, %cond2
+ ret i1 %ret
+}
+
+define i1 @isnan_idiom_wrong_pred2(double %x) {
+; CHECK-LABEL: define i1 @isnan_idiom_wrong_pred2(
+; CHECK-SAME: double [[X:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.fabs.f64(double [[X]])
+; CHECK-NEXT: [[RET:%.*]] = fcmp oeq double [[TMP1]], 0x7FF0000000000000
+; CHECK-NEXT: ret i1 [[RET]]
+;
+ %bits = bitcast double %x to i64
+ %mask1 = and i64 %bits, 9218868437227405312
+ %cond1 = icmp eq i64 %mask1, 9218868437227405312
+ %mask2 = and i64 %bits, 4503599627370495
+ %cond2 = icmp eq i64 %mask2, 0
+ %ret = and i1 %cond1, %cond2
+ ret i1 %ret
+}
+
+define i1 @isnan_idiom_wrong_pred3(double %x) {
+; CHECK-LABEL: define i1 @isnan_idiom_wrong_pred3(
+; CHECK-SAME: double [[X:%.*]]) {
+; CHECK-NEXT: [[BITS:%.*]] = bitcast double [[X]] to i64
+; CHECK-NEXT: [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
+; CHECK-NEXT: [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405312
+; CHECK-NEXT: [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370495
+; CHECK-NEXT: [[COND2:%.*]] = icmp ne i64 [[MASK2]], 0
+; CHECK-NEXT: [[RET:%.*]] = or i1 [[COND1]], [[COND2]]
+; CHECK-NEXT: ret i1 [[RET]]
+;
+ %bits = bitcast double %x to i64
+ %mask1 = and i64 %bits, 9218868437227405312
+ %cond1 = icmp eq i64 %mask1, 9218868437227405312
+ %mask2 = and i64 %bits, 4503599627370495
+ %cond2 = icmp ne i64 %mask2, 0
+ %ret = or i1 %cond1, %cond2
+ ret i1 %ret
+}
+
+define i1 @isnan_idiom_wrong_mask1(double %x) {
+; CHECK-LABEL: define i1 @isnan_idiom_wrong_mask1(
+; CHECK-SAME: double [[X:%.*]]) {
+; CHECK-NEXT: [[BITS:%.*]] = bitcast double [[X]] to i64
+; CHECK-NEXT: [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405311
+; CHECK-NEXT: [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405311
+; CHECK-NEXT: ret i1 [[COND1]]
+;
+ %bits = bitcast double %x to i64
+ %mask1 = and i64 %bits, 9218868437227405311
+ %cond1 = icmp eq i64 %mask1, 9218868437227405311
+ %mask2 = and i64 %bits, 4503599627370495
+ %cond2 = icmp ne i64 %mask2, 0
+ %ret = and i1 %cond1, %cond2
+ ret i1 %ret
+}
+
+define i1 @isnan_idiom_wrong_mask2(double %x) {
+; CHECK-LABEL: define i1 @isnan_idiom_wrong_mask2(
+; CHECK-SAME: double [[X:%.*]]) {
+; CHECK-NEXT: [[BITS:%.*]] = bitcast double [[X]] to i64
+; CHECK-NEXT: [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
+; CHECK-NEXT: [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405312
+; CHECK-NEXT: [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370494
+; CHECK-NEXT: [[COND2:%.*]] = icmp ne i64 [[MASK2]], 0
+; CHECK-NEXT: [[RET:%.*]] = and i1 [[COND1]], [[COND2]]
+; CHECK-NEXT: ret i1 [[RET]]
+;
+ %bits = bitcast double %x to i64
+ %mask1 = and i64 %bits, 9218868437227405312
+ %cond1 = icmp eq i64 %mask1, 9218868437227405312
+ %mask2 = and i64 %bits, 4503599627370494
+ %cond2 = icmp ne i64 %mask2, 0
+ %ret = and i1 %cond1, %cond2
+ ret i1 %ret
+}
+
+define i1 @isnan_idiom_wrong_mask3(double %x) {
+; CHECK-LABEL: define i1 @isnan_idiom_wrong_mask3(
+; CHECK-SAME: double [[X:%.*]]) {
+; CHECK-NEXT: [[BITS:%.*]] = bitcast double [[X]] to i64
+; CHECK-NEXT: [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
+; CHECK-NEXT: [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405312
+; CHECK-NEXT: [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370495
+; CHECK-NEXT: [[COND2:%.*]] = icmp ne i64 [[MASK2]], 4503599627370495
+; CHECK-NEXT: [[RET:%.*]] = and i1 [[COND1]], [[COND2]]
+; CHECK-NEXT: ret i1 [[RET]]
+;
+ %bits = bitcast double %x to i64
+ %mask1 = and i64 %bits, 9218868437227405312
+ %cond1 = icmp eq i64 %mask1, 9218868437227405312
+ %mask2 = and i64 %bits, 4503599627370495
+ %cond2 = icmp ne i64 %mask2, 4503599627370495
+ %ret = and i1 %cond1, %cond2
+ ret i1 %ret
+}
+
+define i1 @isnan_idiom_invalid_bitcast(<2 x float> %x) {
+; CHECK-LABEL: define i1 @isnan_idiom_invalid_bitcast(
+; CHECK-SAME: <2 x float> [[X:%.*]]) {
+; CHECK-NEXT: [[BITS:%.*]] = bitcast <2 x float> [[X]] to i64
+; CHECK-NEXT: [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
+; CHECK-NEXT: [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405312
+; CHECK-NEXT: [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370495
+; CHECK-NEXT: [[COND2:%.*]] = icmp ne i64 [[MASK2]], 0
+; CHECK-NEXT: [[RET:%.*]] = and i1 [[COND1]], [[COND2]]
+; CHECK-NEXT: ret i1 [[RET]]
+;
+ %bits = bitcast <2 x float> %x to i64
+ %mask1 = and i64 %bits, 9218868437227405312
+ %cond1 = icmp eq i64 %mask1, 9218868437227405312
+ %mask2 = and i64 %bits, 4503599627370495
+ %cond2 = icmp ne i64 %mask2, 0
+ %ret = and i1 %cond1, %cond2
+ ret i1 %ret
+}
+
+define i1 @isnan_idiom_ppc_fp128(ppc_fp128 %x) {
+; CHECK-LABEL: define i1 @isnan_idiom_ppc_fp128(
+; CHECK-SAME: ppc_fp128 [[X:%.*]]) {
+; CHECK-NEXT: [[BITS:%.*]] = bitcast ppc_fp128 [[X]] to i128
+; CHECK-NEXT: [[MASK1:%.*]] = and i128 [[BITS]], 170058106710732674489630815774616584192
+; CHECK-NEXT: [[COND1:%.*]] = icmp eq i128 [[MASK1]], 170058106710732674489630815774616584192
+; CHECK-NEXT: [[MASK2:%.*]] = and i128 [[BITS]], 83076749736557242056487941267521535
+; CHECK-NEXT: [[COND2:%.*]] = icmp ne i128 [[MASK2]], 0
+; CHECK-NEXT: [[RET:%.*]] = and i1 [[COND1]], [[COND2]]
+; CHECK-NEXT: ret i1 [[RET]]
+;
+ %bits = bitcast ppc_fp128 %x to i128
+ %mask1 = and i128 %bits, 170058106710732674489630815774616584192
+ %cond1 = icmp eq i128 %mask1, 170058106710732674489630815774616584192
+ %mask2 = and i128 %bits, 83076749736557242056487941267521535
+ %cond2 = icmp ne i128 %mask2, 0
+ %ret = and i1 %cond1, %cond2
+ ret i1 %ret
+}
+
declare void @usei32(i32)
attributes #0 = { noimplicitfloat }
More information about the llvm-commits
mailing list