Skip to content

Commit a10f6c1

Browse files
authored
[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
1 parent e86a92f commit a10f6c1

File tree

2 files changed

+226
-0
lines changed

2 files changed

+226
-0
lines changed

llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3522,6 +3522,23 @@ Value *InstCombinerImpl::foldAndOrOfICmps(ICmpInst *LHS, ICmpInst *RHS,
35223522
}
35233523
}
35243524

3525+
// (X & ExpMask) != 0 && (X & ExpMask) != ExpMask -> isnormal(X)
3526+
// (X & ExpMask) == 0 || (X & ExpMask) == ExpMask -> !isnormal(X)
3527+
Value *X;
3528+
const APInt *MaskC;
3529+
if (LHS0 == RHS0 && PredL == PredR &&
3530+
PredL == (IsAnd ? ICmpInst::ICMP_NE : ICmpInst::ICMP_EQ) &&
3531+
!I.getFunction()->hasFnAttribute(Attribute::NoImplicitFloat) &&
3532+
LHS->hasOneUse() && RHS->hasOneUse() &&
3533+
match(LHS0, m_And(m_ElementWiseBitCast(m_Value(X)), m_APInt(MaskC))) &&
3534+
X->getType()->getScalarType()->isIEEELikeFPTy() &&
3535+
APFloat(X->getType()->getScalarType()->getFltSemantics(), *MaskC)
3536+
.isPosInfinity() &&
3537+
((LHSC->isZero() && *RHSC == *MaskC) ||
3538+
(RHSC->isZero() && *LHSC == *MaskC)))
3539+
return Builder.createIsFPClass(X, IsAnd ? FPClassTest::fcNormal
3540+
: ~FPClassTest::fcNormal);
3541+
35253542
return foldAndOrOfICmpsUsingRanges(LHS, RHS, IsAnd);
35263543
}
35273544

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

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,6 +1013,215 @@ define i1 @isnan_idiom_ppc_fp128(ppc_fp128 %x) {
10131013
ret i1 %ret
10141014
}
10151015

1016+
define i1 @fpclass_test_normal(float %num) {
1017+
; CHECK-LABEL: define i1 @fpclass_test_normal(
1018+
; CHECK-SAME: float [[NUM:%.*]]) {
1019+
; CHECK-NEXT: [[RES:%.*]] = call i1 @llvm.is.fpclass.f32(float [[NUM]], i32 264)
1020+
; CHECK-NEXT: ret i1 [[RES]]
1021+
;
1022+
%cast = bitcast float %num to i32
1023+
%masked = and i32 %cast, 2139095040
1024+
%test1 = icmp ne i32 %masked, 2139095040
1025+
%test2 = icmp ne i32 %masked, 0
1026+
%res = and i1 %test1, %test2
1027+
ret i1 %res
1028+
}
1029+
1030+
define i1 @fpclass_test_normal_half(half %num) {
1031+
; CHECK-LABEL: define i1 @fpclass_test_normal_half(
1032+
; CHECK-SAME: half [[NUM:%.*]]) {
1033+
; CHECK-NEXT: [[RES:%.*]] = call i1 @llvm.is.fpclass.f16(half [[NUM]], i32 264)
1034+
; CHECK-NEXT: ret i1 [[RES]]
1035+
;
1036+
%cast = bitcast half %num to i16
1037+
%masked = and i16 %cast, 31744
1038+
%test1 = icmp ne i16 %masked, 31744
1039+
%test2 = icmp ne i16 %masked, 0
1040+
%res = and i1 %test1, %test2
1041+
ret i1 %res
1042+
}
1043+
1044+
define <2 x i1> @fpclass_test_normal_half_vec(<2 x half> %num) {
1045+
; CHECK-LABEL: define <2 x i1> @fpclass_test_normal_half_vec(
1046+
; CHECK-SAME: <2 x half> [[NUM:%.*]]) {
1047+
; CHECK-NEXT: [[RES:%.*]] = call <2 x i1> @llvm.is.fpclass.v2f16(<2 x half> [[NUM]], i32 264)
1048+
; CHECK-NEXT: ret <2 x i1> [[RES]]
1049+
;
1050+
%cast = bitcast <2 x half> %num to <2 x i16>
1051+
%masked = and <2 x i16> %cast, splat(i16 31744)
1052+
%test1 = icmp ne <2 x i16> %masked, splat(i16 31744)
1053+
%test2 = icmp ne <2 x i16> %masked, zeroinitializer
1054+
%res = and <2 x i1> %test1, %test2
1055+
ret <2 x i1> %res
1056+
}
1057+
1058+
define i1 @fpclass_test_not_normal(float %num) {
1059+
; CHECK-LABEL: define i1 @fpclass_test_not_normal(
1060+
; CHECK-SAME: float [[NUM:%.*]]) {
1061+
; CHECK-NEXT: [[RES:%.*]] = call i1 @llvm.is.fpclass.f32(float [[NUM]], i32 759)
1062+
; CHECK-NEXT: ret i1 [[RES]]
1063+
;
1064+
%cast = bitcast float %num to i32
1065+
%masked = and i32 %cast, 2139095040
1066+
%test1 = icmp eq i32 %masked, 2139095040
1067+
%test2 = icmp eq i32 %masked, 0
1068+
%res = or i1 %test1, %test2
1069+
ret i1 %res
1070+
}
1071+
1072+
define <2 x i1> @fpclass_test_not_normal_vec(<2 x float> %num) {
1073+
; CHECK-LABEL: define <2 x i1> @fpclass_test_not_normal_vec(
1074+
; CHECK-SAME: <2 x float> [[NUM:%.*]]) {
1075+
; CHECK-NEXT: [[RES:%.*]] = call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> [[NUM]], i32 759)
1076+
; CHECK-NEXT: ret <2 x i1> [[RES]]
1077+
;
1078+
%cast = bitcast <2 x float> %num to <2 x i32>
1079+
%masked = and <2 x i32> %cast, splat(i32 2139095040)
1080+
%test1 = icmp eq <2 x i32> %masked, splat(i32 2139095040)
1081+
%test2 = icmp eq <2 x i32> %masked, zeroinitializer
1082+
%res = or <2 x i1> %test1, %test2
1083+
ret <2 x i1> %res
1084+
}
1085+
1086+
define i1 @fpclass_test_normal_commuted(float %num) {
1087+
; CHECK-LABEL: define i1 @fpclass_test_normal_commuted(
1088+
; CHECK-SAME: float [[NUM:%.*]]) {
1089+
; CHECK-NEXT: [[RES:%.*]] = call i1 @llvm.is.fpclass.f32(float [[NUM]], i32 264)
1090+
; CHECK-NEXT: ret i1 [[RES]]
1091+
;
1092+
%cast = bitcast float %num to i32
1093+
%masked = and i32 %cast, 2139095040
1094+
%test1 = icmp ne i32 %masked, 2139095040
1095+
%test2 = icmp ne i32 %masked, 0
1096+
%res = and i1 %test2, %test1
1097+
ret i1 %res
1098+
}
1099+
1100+
; Negative tests
1101+
1102+
define i1 @fpclass_test_normal_fp128(ppc_fp128 %x) {
1103+
; CHECK-LABEL: define i1 @fpclass_test_normal_fp128(
1104+
; CHECK-SAME: ppc_fp128 [[X:%.*]]) {
1105+
; CHECK-NEXT: [[BITS:%.*]] = bitcast ppc_fp128 [[X]] to i128
1106+
; CHECK-NEXT: [[MASKED:%.*]] = and i128 [[BITS]], 170058106710732674489630815774616584192
1107+
; CHECK-NEXT: [[TEST1:%.*]] = icmp ne i128 [[MASKED]], 170058106710732674489630815774616584192
1108+
; CHECK-NEXT: [[TEST2:%.*]] = icmp ne i128 [[MASKED]], 0
1109+
; CHECK-NEXT: [[RES:%.*]] = and i1 [[TEST2]], [[TEST1]]
1110+
; CHECK-NEXT: ret i1 [[RES]]
1111+
;
1112+
%bits = bitcast ppc_fp128 %x to i128
1113+
%masked = and i128 %bits, 170058106710732674489630815774616584192
1114+
%test1 = icmp ne i128 %masked, 170058106710732674489630815774616584192
1115+
%test2 = icmp ne i128 %masked, 0
1116+
%res = and i1 %test2, %test1
1117+
ret i1 %res
1118+
}
1119+
1120+
define i1 @fpclass_test_normal_mismatch_pred(float %num) {
1121+
; CHECK-LABEL: define i1 @fpclass_test_normal_mismatch_pred(
1122+
; CHECK-SAME: float [[NUM:%.*]]) {
1123+
; CHECK-NEXT: [[TEST2:%.*]] = call i1 @llvm.is.fpclass.f32(float [[NUM]], i32 240)
1124+
; CHECK-NEXT: ret i1 [[TEST2]]
1125+
;
1126+
%cast = bitcast float %num to i32
1127+
%masked = and i32 %cast, 2139095040
1128+
%test1 = icmp ne i32 %masked, 2139095040
1129+
%test2 = icmp eq i32 %masked, 0
1130+
%res = and i1 %test1, %test2
1131+
ret i1 %res
1132+
}
1133+
1134+
define i1 @fpclass_test_normal_no_implicit_fp(float %num) #0 {
1135+
; CHECK-LABEL: define i1 @fpclass_test_normal_no_implicit_fp(
1136+
; CHECK-SAME: float [[NUM:%.*]]) #[[ATTR1]] {
1137+
; CHECK-NEXT: [[CAST:%.*]] = bitcast float [[NUM]] to i32
1138+
; CHECK-NEXT: [[MASKED:%.*]] = and i32 [[CAST]], 2139095040
1139+
; CHECK-NEXT: [[TEST1:%.*]] = icmp ne i32 [[MASKED]], 2139095040
1140+
; CHECK-NEXT: [[TEST2:%.*]] = icmp ne i32 [[MASKED]], 0
1141+
; CHECK-NEXT: [[RES:%.*]] = and i1 [[TEST1]], [[TEST2]]
1142+
; CHECK-NEXT: ret i1 [[RES]]
1143+
;
1144+
%cast = bitcast float %num to i32
1145+
%masked = and i32 %cast, 2139095040
1146+
%test1 = icmp ne i32 %masked, 2139095040
1147+
%test2 = icmp ne i32 %masked, 0
1148+
%res = and i1 %test1, %test2
1149+
ret i1 %res
1150+
}
1151+
1152+
define i1 @fpclass_test_normal_invalid_constant1(float %num) {
1153+
; CHECK-LABEL: define i1 @fpclass_test_normal_invalid_constant1(
1154+
; CHECK-SAME: float [[NUM:%.*]]) {
1155+
; CHECK-NEXT: [[CAST:%.*]] = bitcast float [[NUM]] to i32
1156+
; CHECK-NEXT: [[MASKED:%.*]] = and i32 [[CAST]], 2139095039
1157+
; CHECK-NEXT: [[TEST1:%.*]] = icmp ne i32 [[MASKED]], 2139095039
1158+
; CHECK-NEXT: [[TEST2:%.*]] = icmp ne i32 [[MASKED]], 0
1159+
; CHECK-NEXT: [[RES:%.*]] = and i1 [[TEST1]], [[TEST2]]
1160+
; CHECK-NEXT: ret i1 [[RES]]
1161+
;
1162+
%cast = bitcast float %num to i32
1163+
%masked = and i32 %cast, 2139095039
1164+
%test1 = icmp ne i32 %masked, 2139095039
1165+
%test2 = icmp ne i32 %masked, 0
1166+
%res = and i1 %test1, %test2
1167+
ret i1 %res
1168+
}
1169+
1170+
define i1 @fpclass_test_normal_invalid_constant2(float %num) {
1171+
; CHECK-LABEL: define i1 @fpclass_test_normal_invalid_constant2(
1172+
; CHECK-SAME: float [[NUM:%.*]]) {
1173+
; CHECK-NEXT: [[CAST:%.*]] = bitcast float [[NUM]] to i32
1174+
; CHECK-NEXT: [[MASKED:%.*]] = and i32 [[CAST]], 2139095040
1175+
; CHECK-NEXT: [[TEST1:%.*]] = icmp ne i32 [[MASKED]], 2130706432
1176+
; CHECK-NEXT: [[TEST2:%.*]] = icmp ne i32 [[MASKED]], 0
1177+
; CHECK-NEXT: [[RES:%.*]] = and i1 [[TEST1]], [[TEST2]]
1178+
; CHECK-NEXT: ret i1 [[RES]]
1179+
;
1180+
%cast = bitcast float %num to i32
1181+
%masked = and i32 %cast, 2139095040
1182+
%test1 = icmp ne i32 %masked, 2130706432
1183+
%test2 = icmp ne i32 %masked, 0
1184+
%res = and i1 %test1, %test2
1185+
ret i1 %res
1186+
}
1187+
1188+
define i1 @fpclass_test_normal_invalid_constant3(float %num) {
1189+
; CHECK-LABEL: define i1 @fpclass_test_normal_invalid_constant3(
1190+
; CHECK-SAME: float [[NUM:%.*]]) {
1191+
; CHECK-NEXT: [[CAST:%.*]] = bitcast float [[NUM]] to i32
1192+
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[CAST]], 2130706432
1193+
; CHECK-NEXT: [[RES:%.*]] = icmp ne i32 [[TMP1]], 2130706432
1194+
; CHECK-NEXT: ret i1 [[RES]]
1195+
;
1196+
%cast = bitcast float %num to i32
1197+
%masked = and i32 %cast, 2139095040
1198+
%test1 = icmp ne i32 %masked, 2139095040
1199+
%test2 = icmp ne i32 %masked, 2130706432
1200+
%res = and i1 %test1, %test2
1201+
ret i1 %res
1202+
}
1203+
1204+
define i1 @fpclass_test_normal_multiuse(float %num) {
1205+
; CHECK-LABEL: define i1 @fpclass_test_normal_multiuse(
1206+
; CHECK-SAME: float [[NUM:%.*]]) {
1207+
; CHECK-NEXT: [[CAST:%.*]] = bitcast float [[NUM]] to i32
1208+
; CHECK-NEXT: [[MASKED:%.*]] = and i32 [[CAST]], 2139095040
1209+
; CHECK-NEXT: [[TEST1:%.*]] = icmp ne i32 [[MASKED]], 2139095040
1210+
; CHECK-NEXT: [[TEST2:%.*]] = icmp ne i32 [[MASKED]], 0
1211+
; CHECK-NEXT: call void @usei1(i1 [[TEST1]])
1212+
; CHECK-NEXT: [[RES:%.*]] = and i1 [[TEST1]], [[TEST2]]
1213+
; CHECK-NEXT: ret i1 [[RES]]
1214+
;
1215+
%cast = bitcast float %num to i32
1216+
%masked = and i32 %cast, 2139095040
1217+
%test1 = icmp ne i32 %masked, 2139095040
1218+
%test2 = icmp ne i32 %masked, 0
1219+
call void @usei1(i1 %test1)
1220+
%res = and i1 %test1, %test2
1221+
ret i1 %res
1222+
}
1223+
10161224
declare void @usei32(i32)
1225+
declare void @usei1(i1)
10171226

10181227
attributes #0 = { noimplicitfloat }

0 commit comments

Comments
 (0)