Skip to content

Commit dba3dfa

Browse files
authored
[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
1 parent 23609a3 commit dba3dfa

File tree

2 files changed

+268
-20
lines changed

2 files changed

+268
-20
lines changed

llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -326,8 +326,8 @@ static std::optional<std::pair<unsigned, unsigned>> getMaskedTypeForICmpPair(
326326
/// (icmp (A & 12) != 0) & (icmp (A & 15) == 8) -> (icmp (A & 15) == 8).
327327
/// Also used for logical and/or, must be poison safe.
328328
static Value *foldLogOpOfMaskedICmps_NotAllZeros_BMask_Mixed(
329-
ICmpInst *LHS, ICmpInst *RHS, bool IsAnd, Value *A, Value *B, Value *C,
330-
Value *D, Value *E, ICmpInst::Predicate PredL, ICmpInst::Predicate PredR,
329+
ICmpInst *LHS, ICmpInst *RHS, bool IsAnd, Value *A, Value *B, Value *D,
330+
Value *E, ICmpInst::Predicate PredL, ICmpInst::Predicate PredR,
331331
InstCombiner::BuilderTy &Builder) {
332332
// We are given the canonical form:
333333
// (icmp ne (A & B), 0) & (icmp eq (A & D), E).
@@ -339,9 +339,9 @@ static Value *foldLogOpOfMaskedICmps_NotAllZeros_BMask_Mixed(
339339
//
340340
// We currently handle the case of B, C, D, E are constant.
341341
//
342-
const APInt *BCst, *CCst, *DCst, *OrigECst;
343-
if (!match(B, m_APInt(BCst)) || !match(C, m_APInt(CCst)) ||
344-
!match(D, m_APInt(DCst)) || !match(E, m_APInt(OrigECst)))
342+
const APInt *BCst, *DCst, *OrigECst;
343+
if (!match(B, m_APInt(BCst)) || !match(D, m_APInt(DCst)) ||
344+
!match(E, m_APInt(OrigECst)))
345345
return nullptr;
346346

347347
ICmpInst::Predicate NewCC = IsAnd ? ICmpInst::ICMP_EQ : ICmpInst::ICMP_NE;
@@ -359,12 +359,32 @@ static Value *foldLogOpOfMaskedICmps_NotAllZeros_BMask_Mixed(
359359
if (*BCst == 0 || *DCst == 0)
360360
return nullptr;
361361

362-
// If B and D don't intersect, ie. (B & D) == 0, no folding because we can't
363-
// deduce anything from it.
364-
// For example,
365-
// (icmp ne (A & 12), 0) & (icmp eq (A & 3), 1) -> no folding.
366-
if ((*BCst & *DCst) == 0)
362+
// If B and D don't intersect, ie. (B & D) == 0, try to fold isNaN idiom:
363+
// (icmp ne (A & FractionBits), 0) & (icmp eq (A & ExpBits), ExpBits)
364+
// -> isNaN(A)
365+
// Otherwise, we cannot deduce anything from it.
366+
if (!BCst->intersects(*DCst)) {
367+
Value *Src;
368+
if (*DCst == ECst && match(A, m_ElementWiseBitCast(m_Value(Src))) &&
369+
!Builder.GetInsertBlock()->getParent()->hasFnAttribute(
370+
Attribute::StrictFP)) {
371+
Type *Ty = Src->getType()->getScalarType();
372+
if (!Ty->isIEEELikeFPTy())
373+
return nullptr;
374+
375+
APInt ExpBits = APFloat::getInf(Ty->getFltSemantics()).bitcastToAPInt();
376+
if (ECst != ExpBits)
377+
return nullptr;
378+
APInt FractionBits = ~ExpBits;
379+
FractionBits.clearSignBit();
380+
if (*BCst != FractionBits)
381+
return nullptr;
382+
383+
return Builder.CreateFCmp(IsAnd ? FCmpInst::FCMP_UNO : FCmpInst::FCMP_ORD,
384+
Src, ConstantFP::getZero(Src->getType()));
385+
}
367386
return nullptr;
387+
}
368388

369389
// If the following two conditions are met:
370390
//
@@ -464,14 +484,12 @@ static Value *foldLogOpOfMaskedICmpsAsymmetric(
464484
}
465485
if ((LHSMask & Mask_NotAllZeros) && (RHSMask & BMask_Mixed)) {
466486
if (Value *V = foldLogOpOfMaskedICmps_NotAllZeros_BMask_Mixed(
467-
LHS, RHS, IsAnd, A, B, C, D, E,
468-
PredL, PredR, Builder)) {
487+
LHS, RHS, IsAnd, A, B, D, E, PredL, PredR, Builder)) {
469488
return V;
470489
}
471490
} else if ((LHSMask & BMask_Mixed) && (RHSMask & Mask_NotAllZeros)) {
472491
if (Value *V = foldLogOpOfMaskedICmps_NotAllZeros_BMask_Mixed(
473-
RHS, LHS, IsAnd, A, D, E, B, C,
474-
PredR, PredL, Builder)) {
492+
RHS, LHS, IsAnd, A, D, B, C, PredR, PredL, Builder)) {
475493
return V;
476494
}
477495
}

llvm/test/Transforms/InstCombine/fpclass-check-idioms.ll

Lines changed: 236 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -217,12 +217,7 @@ define i1 @f32_fczero_strictfp(float %a) strictfp {
217217
define i1 @f32_fcnan(float %a) {
218218
; CHECK-LABEL: define i1 @f32_fcnan(
219219
; CHECK-SAME: float [[A:%.*]]) {
220-
; CHECK-NEXT: [[I32:%.*]] = bitcast float [[A]] to i32
221-
; CHECK-NEXT: [[AND1:%.*]] = and i32 [[I32]], 2139095040
222-
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[AND1]], 2139095040
223-
; CHECK-NEXT: [[AND2:%.*]] = and i32 [[I32]], 8388607
224-
; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i32 [[AND2]], 0
225-
; CHECK-NEXT: [[RES:%.*]] = and i1 [[CMP1]], [[CMP2]]
220+
; CHECK-NEXT: [[RES:%.*]] = fcmp uno float [[A]], 0.000000e+00
226221
; CHECK-NEXT: ret i1 [[RES]]
227222
;
228223
%i32 = bitcast float %a to i32
@@ -647,6 +642,241 @@ define i1 @f32_fcposinf_multiuse_strictfp(float %a) strictfp {
647642
ret i1 %cmp
648643
}
649644

645+
define i1 @isnan_idiom(double %x) {
646+
; CHECK-LABEL: define i1 @isnan_idiom(
647+
; CHECK-SAME: double [[X:%.*]]) {
648+
; CHECK-NEXT: [[RET:%.*]] = fcmp uno double [[X]], 0.000000e+00
649+
; CHECK-NEXT: ret i1 [[RET]]
650+
;
651+
%bits = bitcast double %x to i64
652+
%mask1 = and i64 %bits, 9218868437227405312
653+
%cond1 = icmp eq i64 %mask1, 9218868437227405312
654+
%mask2 = and i64 %bits, 4503599627370495
655+
%cond2 = icmp ne i64 %mask2, 0
656+
%ret = and i1 %cond1, %cond2
657+
ret i1 %ret
658+
}
659+
660+
define <2 x i1> @isnan_idiom_vec(<2 x double> %x) {
661+
; CHECK-LABEL: define <2 x i1> @isnan_idiom_vec(
662+
; CHECK-SAME: <2 x double> [[X:%.*]]) {
663+
; CHECK-NEXT: [[RET:%.*]] = fcmp uno <2 x double> [[X]], zeroinitializer
664+
; CHECK-NEXT: ret <2 x i1> [[RET]]
665+
;
666+
%bits = bitcast <2 x double> %x to <2 x i64>
667+
%mask1 = and <2 x i64> %bits, splat(i64 9218868437227405312)
668+
%cond1 = icmp eq <2 x i64> %mask1, splat(i64 9218868437227405312)
669+
%mask2 = and <2 x i64> %bits, splat(i64 4503599627370495)
670+
%cond2 = icmp ne <2 x i64> %mask2, zeroinitializer
671+
%ret = and <2 x i1> %cond1, %cond2
672+
ret <2 x i1> %ret
673+
}
674+
675+
define i1 @isnan_idiom_commuted(double %x) {
676+
; CHECK-LABEL: define i1 @isnan_idiom_commuted(
677+
; CHECK-SAME: double [[X:%.*]]) {
678+
; CHECK-NEXT: [[RET:%.*]] = fcmp uno double [[X]], 0.000000e+00
679+
; CHECK-NEXT: ret i1 [[RET]]
680+
;
681+
%bits = bitcast double %x to i64
682+
%mask1 = and i64 %bits, 9218868437227405312
683+
%cond1 = icmp eq i64 %mask1, 9218868437227405312
684+
%mask2 = and i64 %bits, 4503599627370495
685+
%cond2 = icmp ne i64 %mask2, 0
686+
%ret = and i1 %cond2, %cond1
687+
ret i1 %ret
688+
}
689+
690+
define i1 @isnotnan_idiom(double %x) {
691+
; CHECK-LABEL: define i1 @isnotnan_idiom(
692+
; CHECK-SAME: double [[X:%.*]]) {
693+
; CHECK-NEXT: [[RET:%.*]] = fcmp ord double [[X]], 0.000000e+00
694+
; CHECK-NEXT: ret i1 [[RET]]
695+
;
696+
%bits = bitcast double %x to i64
697+
%mask1 = and i64 %bits, 9218868437227405312
698+
%cond1 = icmp ne i64 %mask1, 9218868437227405312
699+
%mask2 = and i64 %bits, 4503599627370495
700+
%cond2 = icmp eq i64 %mask2, 0
701+
%ret = or i1 %cond1, %cond2
702+
ret i1 %ret
703+
}
704+
705+
; negative tests
706+
707+
define i1 @isnan_idiom_strictfp(double %x) strictfp {
708+
; CHECK-LABEL: define i1 @isnan_idiom_strictfp(
709+
; CHECK-SAME: double [[X:%.*]]) #[[ATTR0]] {
710+
; CHECK-NEXT: [[BITS:%.*]] = bitcast double [[X]] to i64
711+
; CHECK-NEXT: [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
712+
; CHECK-NEXT: [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405312
713+
; CHECK-NEXT: [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370495
714+
; CHECK-NEXT: [[COND2:%.*]] = icmp ne i64 [[MASK2]], 0
715+
; CHECK-NEXT: [[RET:%.*]] = and i1 [[COND1]], [[COND2]]
716+
; CHECK-NEXT: ret i1 [[RET]]
717+
;
718+
%bits = bitcast double %x to i64
719+
%mask1 = and i64 %bits, 9218868437227405312
720+
%cond1 = icmp eq i64 %mask1, 9218868437227405312
721+
%mask2 = and i64 %bits, 4503599627370495
722+
%cond2 = icmp ne i64 %mask2, 0
723+
%ret = and i1 %cond1, %cond2
724+
ret i1 %ret
725+
}
726+
727+
define i1 @isnan_idiom_wrong_pred1(double %x) {
728+
; CHECK-LABEL: define i1 @isnan_idiom_wrong_pred1(
729+
; CHECK-SAME: double [[X:%.*]]) {
730+
; CHECK-NEXT: [[BITS:%.*]] = bitcast double [[X]] to i64
731+
; CHECK-NEXT: [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
732+
; CHECK-NEXT: [[COND1:%.*]] = icmp ne i64 [[MASK1]], 9218868437227405312
733+
; CHECK-NEXT: [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370495
734+
; CHECK-NEXT: [[COND2:%.*]] = icmp ne i64 [[MASK2]], 0
735+
; CHECK-NEXT: [[RET:%.*]] = and i1 [[COND1]], [[COND2]]
736+
; CHECK-NEXT: ret i1 [[RET]]
737+
;
738+
%bits = bitcast double %x to i64
739+
%mask1 = and i64 %bits, 9218868437227405312
740+
%cond1 = icmp ne i64 %mask1, 9218868437227405312
741+
%mask2 = and i64 %bits, 4503599627370495
742+
%cond2 = icmp ne i64 %mask2, 0
743+
%ret = and i1 %cond1, %cond2
744+
ret i1 %ret
745+
}
746+
747+
define i1 @isnan_idiom_wrong_pred2(double %x) {
748+
; CHECK-LABEL: define i1 @isnan_idiom_wrong_pred2(
749+
; CHECK-SAME: double [[X:%.*]]) {
750+
; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.fabs.f64(double [[X]])
751+
; CHECK-NEXT: [[RET:%.*]] = fcmp oeq double [[TMP1]], 0x7FF0000000000000
752+
; CHECK-NEXT: ret i1 [[RET]]
753+
;
754+
%bits = bitcast double %x to i64
755+
%mask1 = and i64 %bits, 9218868437227405312
756+
%cond1 = icmp eq i64 %mask1, 9218868437227405312
757+
%mask2 = and i64 %bits, 4503599627370495
758+
%cond2 = icmp eq i64 %mask2, 0
759+
%ret = and i1 %cond1, %cond2
760+
ret i1 %ret
761+
}
762+
763+
define i1 @isnan_idiom_wrong_pred3(double %x) {
764+
; CHECK-LABEL: define i1 @isnan_idiom_wrong_pred3(
765+
; CHECK-SAME: double [[X:%.*]]) {
766+
; CHECK-NEXT: [[BITS:%.*]] = bitcast double [[X]] to i64
767+
; CHECK-NEXT: [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
768+
; CHECK-NEXT: [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405312
769+
; CHECK-NEXT: [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370495
770+
; CHECK-NEXT: [[COND2:%.*]] = icmp ne i64 [[MASK2]], 0
771+
; CHECK-NEXT: [[RET:%.*]] = or i1 [[COND1]], [[COND2]]
772+
; CHECK-NEXT: ret i1 [[RET]]
773+
;
774+
%bits = bitcast double %x to i64
775+
%mask1 = and i64 %bits, 9218868437227405312
776+
%cond1 = icmp eq i64 %mask1, 9218868437227405312
777+
%mask2 = and i64 %bits, 4503599627370495
778+
%cond2 = icmp ne i64 %mask2, 0
779+
%ret = or i1 %cond1, %cond2
780+
ret i1 %ret
781+
}
782+
783+
define i1 @isnan_idiom_wrong_mask1(double %x) {
784+
; CHECK-LABEL: define i1 @isnan_idiom_wrong_mask1(
785+
; CHECK-SAME: double [[X:%.*]]) {
786+
; CHECK-NEXT: [[BITS:%.*]] = bitcast double [[X]] to i64
787+
; CHECK-NEXT: [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405311
788+
; CHECK-NEXT: [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405311
789+
; CHECK-NEXT: ret i1 [[COND1]]
790+
;
791+
%bits = bitcast double %x to i64
792+
%mask1 = and i64 %bits, 9218868437227405311
793+
%cond1 = icmp eq i64 %mask1, 9218868437227405311
794+
%mask2 = and i64 %bits, 4503599627370495
795+
%cond2 = icmp ne i64 %mask2, 0
796+
%ret = and i1 %cond1, %cond2
797+
ret i1 %ret
798+
}
799+
800+
define i1 @isnan_idiom_wrong_mask2(double %x) {
801+
; CHECK-LABEL: define i1 @isnan_idiom_wrong_mask2(
802+
; CHECK-SAME: double [[X:%.*]]) {
803+
; CHECK-NEXT: [[BITS:%.*]] = bitcast double [[X]] to i64
804+
; CHECK-NEXT: [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
805+
; CHECK-NEXT: [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405312
806+
; CHECK-NEXT: [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370494
807+
; CHECK-NEXT: [[COND2:%.*]] = icmp ne i64 [[MASK2]], 0
808+
; CHECK-NEXT: [[RET:%.*]] = and i1 [[COND1]], [[COND2]]
809+
; CHECK-NEXT: ret i1 [[RET]]
810+
;
811+
%bits = bitcast double %x to i64
812+
%mask1 = and i64 %bits, 9218868437227405312
813+
%cond1 = icmp eq i64 %mask1, 9218868437227405312
814+
%mask2 = and i64 %bits, 4503599627370494
815+
%cond2 = icmp ne i64 %mask2, 0
816+
%ret = and i1 %cond1, %cond2
817+
ret i1 %ret
818+
}
819+
820+
define i1 @isnan_idiom_wrong_mask3(double %x) {
821+
; CHECK-LABEL: define i1 @isnan_idiom_wrong_mask3(
822+
; CHECK-SAME: double [[X:%.*]]) {
823+
; CHECK-NEXT: [[BITS:%.*]] = bitcast double [[X]] to i64
824+
; CHECK-NEXT: [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
825+
; CHECK-NEXT: [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405312
826+
; CHECK-NEXT: [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370495
827+
; CHECK-NEXT: [[COND2:%.*]] = icmp ne i64 [[MASK2]], 4503599627370495
828+
; CHECK-NEXT: [[RET:%.*]] = and i1 [[COND1]], [[COND2]]
829+
; CHECK-NEXT: ret i1 [[RET]]
830+
;
831+
%bits = bitcast double %x to i64
832+
%mask1 = and i64 %bits, 9218868437227405312
833+
%cond1 = icmp eq i64 %mask1, 9218868437227405312
834+
%mask2 = and i64 %bits, 4503599627370495
835+
%cond2 = icmp ne i64 %mask2, 4503599627370495
836+
%ret = and i1 %cond1, %cond2
837+
ret i1 %ret
838+
}
839+
840+
define i1 @isnan_idiom_invalid_bitcast(<2 x float> %x) {
841+
; CHECK-LABEL: define i1 @isnan_idiom_invalid_bitcast(
842+
; CHECK-SAME: <2 x float> [[X:%.*]]) {
843+
; CHECK-NEXT: [[BITS:%.*]] = bitcast <2 x float> [[X]] to i64
844+
; CHECK-NEXT: [[MASK1:%.*]] = and i64 [[BITS]], 9218868437227405312
845+
; CHECK-NEXT: [[COND1:%.*]] = icmp eq i64 [[MASK1]], 9218868437227405312
846+
; CHECK-NEXT: [[MASK2:%.*]] = and i64 [[BITS]], 4503599627370495
847+
; CHECK-NEXT: [[COND2:%.*]] = icmp ne i64 [[MASK2]], 0
848+
; CHECK-NEXT: [[RET:%.*]] = and i1 [[COND1]], [[COND2]]
849+
; CHECK-NEXT: ret i1 [[RET]]
850+
;
851+
%bits = bitcast <2 x float> %x to i64
852+
%mask1 = and i64 %bits, 9218868437227405312
853+
%cond1 = icmp eq i64 %mask1, 9218868437227405312
854+
%mask2 = and i64 %bits, 4503599627370495
855+
%cond2 = icmp ne i64 %mask2, 0
856+
%ret = and i1 %cond1, %cond2
857+
ret i1 %ret
858+
}
859+
860+
define i1 @isnan_idiom_ppc_fp128(ppc_fp128 %x) {
861+
; CHECK-LABEL: define i1 @isnan_idiom_ppc_fp128(
862+
; CHECK-SAME: ppc_fp128 [[X:%.*]]) {
863+
; CHECK-NEXT: [[BITS:%.*]] = bitcast ppc_fp128 [[X]] to i128
864+
; CHECK-NEXT: [[MASK1:%.*]] = and i128 [[BITS]], 170058106710732674489630815774616584192
865+
; CHECK-NEXT: [[COND1:%.*]] = icmp eq i128 [[MASK1]], 170058106710732674489630815774616584192
866+
; CHECK-NEXT: [[MASK2:%.*]] = and i128 [[BITS]], 83076749736557242056487941267521535
867+
; CHECK-NEXT: [[COND2:%.*]] = icmp ne i128 [[MASK2]], 0
868+
; CHECK-NEXT: [[RET:%.*]] = and i1 [[COND1]], [[COND2]]
869+
; CHECK-NEXT: ret i1 [[RET]]
870+
;
871+
%bits = bitcast ppc_fp128 %x to i128
872+
%mask1 = and i128 %bits, 170058106710732674489630815774616584192
873+
%cond1 = icmp eq i128 %mask1, 170058106710732674489630815774616584192
874+
%mask2 = and i128 %bits, 83076749736557242056487941267521535
875+
%cond2 = icmp ne i128 %mask2, 0
876+
%ret = and i1 %cond1, %cond2
877+
ret i1 %ret
878+
}
879+
650880
declare void @usei32(i32)
651881

652882
attributes #0 = { noimplicitfloat }

0 commit comments

Comments
 (0)