Skip to content

Commit c06a312

Browse files
committed
[InstCombine] Generalise optimisation of redundant floating point comparisons with ConstantFPRange
Follow up of #158097 Similar to `simplifyAndOrOfICmpsWithConstants`, we can do so for floating point comparisons.
1 parent 46ad540 commit c06a312

File tree

3 files changed

+75
-47
lines changed

3 files changed

+75
-47
lines changed

llvm/lib/Analysis/InstructionSimplify.cpp

Lines changed: 58 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "llvm/Analysis/TargetLibraryInfo.h"
3636
#include "llvm/Analysis/ValueTracking.h"
3737
#include "llvm/Analysis/VectorUtils.h"
38+
#include "llvm/IR/ConstantFPRange.h"
3839
#include "llvm/IR/ConstantRange.h"
3940
#include "llvm/IR/DataLayout.h"
4041
#include "llvm/IR/Dominators.h"
@@ -1812,6 +1813,61 @@ static Value *simplifyOrOfICmps(ICmpInst *Op0, ICmpInst *Op1,
18121813
return nullptr;
18131814
}
18141815

1816+
/// Test if a pair of compares with a shared operand and 2 constants has an
1817+
/// empty set intersection, full set union, or if one compare is a superset of
1818+
/// the other.
1819+
static Value *simplifyAndOrOfFCmpsWithConstants(FCmpInst *Cmp0, FCmpInst *Cmp1,
1820+
bool IsAnd) {
1821+
// Look for this pattern: {and/or} (fcmp X, C0), (fcmp X, C1)).
1822+
if (Cmp0->getOperand(0) != Cmp1->getOperand(0))
1823+
return nullptr;
1824+
1825+
const APFloat *C0, *C1;
1826+
if (!match(Cmp0->getOperand(1), m_APFloat(C0)) ||
1827+
!match(Cmp1->getOperand(1), m_APFloat(C1)))
1828+
return nullptr;
1829+
1830+
auto Range0 = ConstantFPRange::makeExactFCmpRegion(Cmp0->getPredicate(), *C0);
1831+
auto Range1 = ConstantFPRange::makeExactFCmpRegion(Cmp1->getPredicate(), *C1);
1832+
1833+
if (!Range0 || !Range1)
1834+
return nullptr;
1835+
1836+
// For and-of-compares, check if the intersection is empty:
1837+
// (fcmp X, C0) && (fcmp X, C1) --> empty set --> false
1838+
if (IsAnd && (*Range0).intersectWith(*Range1).isEmptySet())
1839+
return getFalse(Cmp0->getType());
1840+
1841+
// For or-of-compares, check if the union is full:
1842+
// (fcmp X, C0) || (fcmp X, C1) --> full set --> true
1843+
//
1844+
// TODO: `unionWith` is not precise at the moment, so
1845+
// we can invert the predicate and check:
1846+
// inv(fcmp X, C0) && inv(fcmp X, C1) --> empty set --> false
1847+
if (!IsAnd) {
1848+
auto Range0Inv = ConstantFPRange::makeExactFCmpRegion(
1849+
FCmpInst::getInversePredicate(Cmp0->getPredicate()), *C0);
1850+
auto Range1Inv = ConstantFPRange::makeExactFCmpRegion(
1851+
FCmpInst::getInversePredicate(Cmp1->getPredicate()), *C1);
1852+
if (Range0Inv && Range1Inv) {
1853+
if ((*Range0Inv).intersectWith(*Range1Inv).isEmptySet())
1854+
return getFalse(Cmp0->getType());
1855+
}
1856+
}
1857+
1858+
// Is one range a superset of the other?
1859+
// If this is and-of-compares, take the smaller set:
1860+
// (fcmp ogt X, 4) && (fcmp ogt X, 42) --> fcmp ogt X, 42
1861+
// If this is or-of-compares, take the larger set:
1862+
// (fcmp ogt X, 4) || (fcmp ogt X, 42) --> fcmp ogt X, 4
1863+
if ((*Range0).contains(*Range1))
1864+
return IsAnd ? Cmp1 : Cmp0;
1865+
if ((*Range1).contains(*Range0))
1866+
return IsAnd ? Cmp0 : Cmp1;
1867+
1868+
return nullptr;
1869+
}
1870+
18151871
static Value *simplifyAndOrOfFCmps(const SimplifyQuery &Q, FCmpInst *LHS,
18161872
FCmpInst *RHS, bool IsAnd) {
18171873
Value *LHS0 = LHS->getOperand(0), *LHS1 = LHS->getOperand(1);
@@ -1850,34 +1906,8 @@ static Value *simplifyAndOrOfFCmps(const SimplifyQuery &Q, FCmpInst *LHS,
18501906
: ConstantInt::getBool(LHS->getType(), !IsAnd);
18511907
}
18521908

1853-
Value *V0;
1854-
const APFloat *V0Op1, *V1Op1;
1855-
// (fcmp olt V0, V0Op1) || (fcmp olt V0, V1Op1)
1856-
// --> fcmp olt V0, max(V0Op1, V1Op1)
1857-
// (fcmp ogt V0, V0Op1) || (fcmp ogt V0, V1Op1)
1858-
// --> fcmp ogt V0, max(V0Op1, V1Op1)
1859-
//
1860-
// (fcmp olt V0, V0Op1) && (fcmp olt V0, V1Op1)
1861-
// --> fcmp olt V0, min(V0Op1, V1Op1)
1862-
// (fcmp ogt V0, V0Op1) && (fcmp ogt V0, V1Op1)
1863-
// --> fcmp ogt V0, min(V0Op1, V1Op1)
1864-
if (match(LHS, m_SpecificFCmp(FCmpInst::FCMP_OLT, m_Value(V0),
1865-
m_APFloat(V0Op1))) &&
1866-
match(RHS, m_SpecificFCmp(FCmpInst::FCMP_OLT, m_Specific(V0),
1867-
m_APFloat(V1Op1)))) {
1868-
if (*V0Op1 > *V1Op1)
1869-
return IsAnd ? RHS : LHS;
1870-
if (*V1Op1 > *V0Op1)
1871-
return IsAnd ? LHS : RHS;
1872-
} else if (match(LHS, m_SpecificFCmp(FCmpInst::FCMP_OGT, m_Value(V0),
1873-
m_APFloat(V0Op1))) &&
1874-
match(RHS, m_SpecificFCmp(FCmpInst::FCMP_OGT, m_Specific(V0),
1875-
m_APFloat(V1Op1)))) {
1876-
if (*V0Op1 < *V1Op1)
1877-
return IsAnd ? RHS : LHS;
1878-
if (*V1Op1 < *V0Op1)
1879-
return IsAnd ? LHS : RHS;
1880-
}
1909+
if (auto *V = simplifyAndOrOfFCmpsWithConstants(LHS, RHS, IsAnd))
1910+
return V;
18811911

18821912
return nullptr;
18831913
}

llvm/test/Transforms/InstCombine/create-class-from-logic-fcmp.ll

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,8 @@ define i1 @not_issubnormal_or_inf(half %x) {
567567

568568
define i1 @issubnormal_uge_or_inf(half %x) {
569569
; CHECK-LABEL: @issubnormal_uge_or_inf(
570-
; CHECK-NEXT: [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X:%.*]], i32 783)
570+
; CHECK-NEXT: [[FABS:%.*]] = call half @llvm.fabs.f16(half [[X:%.*]])
571+
; CHECK-NEXT: [[CLASS:%.*]] = fcmp uge half [[FABS]], 0xH0400
571572
; CHECK-NEXT: ret i1 [[CLASS]]
572573
;
573574
%fabs = call half @llvm.fabs.f16(half %x)
@@ -609,10 +610,8 @@ define i1 @issubnormal_or_inf_neg_smallest_normal(half %x) {
609610
define i1 @fneg_fabs_olt_neg_smallest_normal_or_inf(half %x) {
610611
; CHECK-LABEL: @fneg_fabs_olt_neg_smallest_normal_or_inf(
611612
; CHECK-NEXT: [[FABS:%.*]] = call half @llvm.fabs.f16(half [[X:%.*]])
612-
; CHECK-NEXT: [[CMPINF:%.*]] = fcmp oeq half [[FABS]], 0xH7C00
613613
; CHECK-NEXT: [[CMP_SMALLEST_NORMAL:%.*]] = fcmp ogt half [[FABS]], 0xH0400
614-
; CHECK-NEXT: [[CLASS:%.*]] = or i1 [[CMP_SMALLEST_NORMAL]], [[CMPINF]]
615-
; CHECK-NEXT: ret i1 [[CLASS]]
614+
; CHECK-NEXT: ret i1 [[CMP_SMALLEST_NORMAL]]
616615
;
617616
%fabs = call half @llvm.fabs.f16(half %x)
618617
%cmpinf = fcmp oeq half %fabs, 0xH7C00
@@ -674,7 +673,8 @@ define i1 @not_zero_and_subnormal(half %x) {
674673

675674
define i1 @fcmp_fabs_uge_inf_or_fabs_uge_smallest_norm(half %x) {
676675
; CHECK-LABEL: @fcmp_fabs_uge_inf_or_fabs_uge_smallest_norm(
677-
; CHECK-NEXT: [[OR:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X:%.*]], i32 783)
676+
; CHECK-NEXT: [[FABS:%.*]] = call half @llvm.fabs.f16(half [[X:%.*]])
677+
; CHECK-NEXT: [[OR:%.*]] = fcmp uge half [[FABS]], 0xH0400
678678
; CHECK-NEXT: ret i1 [[OR]]
679679
;
680680
%fabs = call half @llvm.fabs.f16(half %x)
@@ -868,7 +868,8 @@ define i1 @une_or_oge_smallest_normal(half %x) #0 {
868868
; -> normal | inf
869869
define i1 @isnormalinf_or_inf(half %x) #0 {
870870
; CHECK-LABEL: @isnormalinf_or_inf(
871-
; CHECK-NEXT: [[OR:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X:%.*]], i32 780)
871+
; CHECK-NEXT: [[FABS:%.*]] = call half @llvm.fabs.f16(half [[X:%.*]])
872+
; CHECK-NEXT: [[OR:%.*]] = fcmp oge half [[FABS]], 0xH0400
872873
; CHECK-NEXT: ret i1 [[OR]]
873874
;
874875
%fabs = call half @llvm.fabs.f16(half %x)
@@ -1408,7 +1409,7 @@ define i1 @oeq_neginfinity_or_oeq_smallest_normal(half %x) #0 {
14081409
; -> ninf | fcZero | fcSubnormal
14091410
define i1 @oeq_neginfinity_or_olt_smallest_normal(half %x) #0 {
14101411
; CHECK-LABEL: @oeq_neginfinity_or_olt_smallest_normal(
1411-
; CHECK-NEXT: [[CLASS:%.*]] = call i1 @llvm.is.fpclass.f16(half [[X:%.*]], i32 252)
1412+
; CHECK-NEXT: [[CLASS:%.*]] = fcmp olt half [[X:%.*]], 0xH0400
14121413
; CHECK-NEXT: ret i1 [[CLASS]]
14131414
;
14141415
%oeq.neg.infinity = fcmp oeq half %x, 0xHFC00

llvm/test/Transforms/InstCombine/redundant-fcmp.ll

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ define i1 @or_fcmp_redundant_or4(double %v0) {
4545
ret i1 %v3
4646
}
4747

48-
define i1 @or_fcmp_redundant_or_neg1(double %v0) {
49-
; CHECK-LABEL: @or_fcmp_redundant_or_neg1(
48+
define i1 @or_fcmp_redundant_or_5(double %v0) {
49+
; CHECK-LABEL: @or_fcmp_redundant_or_5(
5050
; CHECK-NEXT: [[V1:%.*]] = fcmp olt double [[V0:%.*]], 1.000000e-02
5151
; CHECK-NEXT: [[V2:%.*]] = fcmp ogt double [[V0]], 1.990000e+00
5252
; CHECK-NEXT: [[V3:%.*]] = or i1 [[V1]], [[V2]]
@@ -58,8 +58,8 @@ define i1 @or_fcmp_redundant_or_neg1(double %v0) {
5858
ret i1 %v3
5959
}
6060

61-
define i1 @or_fcmp_redundant_or_neg2(double %v0) {
62-
; CHECK-LABEL: @or_fcmp_redundant_or_neg2(
61+
define i1 @or_fcmp_redundant_or_6(double %v0) {
62+
; CHECK-LABEL: @or_fcmp_redundant_or_6(
6363
; CHECK-NEXT: [[V1:%.*]] = fcmp ogt double [[V0:%.*]], 1.000000e-02
6464
; CHECK-NEXT: [[V2:%.*]] = fcmp olt double [[V0]], 1.990000e+00
6565
; CHECK-NEXT: [[V3:%.*]] = or i1 [[V1]], [[V2]]
@@ -115,21 +115,18 @@ define i1 @or_fcmp_redundant_and4(double %v0) {
115115
ret i1 %v3
116116
}
117117

118-
define i1 @or_fcmp_redundant_and_neg1(double %v0) {
119-
; CHECK-LABEL: @or_fcmp_redundant_and_neg1(
120-
; CHECK-NEXT: [[V1:%.*]] = fcmp olt double [[V0:%.*]], 1.000000e-02
121-
; CHECK-NEXT: [[V2:%.*]] = fcmp ogt double [[V0]], 1.990000e+00
122-
; CHECK-NEXT: [[V3:%.*]] = and i1 [[V1]], [[V2]]
123-
; CHECK-NEXT: ret i1 [[V3]]
118+
define i1 @or_fcmp_redundant_and_5(double %v0) {
119+
; CHECK-LABEL: @or_fcmp_redundant_and_5(
120+
; CHECK-NEXT: ret i1 false
124121
;
125122
%v1 = fcmp olt double %v0, 1.000000e-02
126123
%v2 = fcmp ogt double %v0, 1.990000e+00
127124
%v3 = and i1 %v1, %v2
128125
ret i1 %v3
129126
}
130127

131-
define i1 @or_fcmp_redundant_and_neg2(double %v0) {
132-
; CHECK-LABEL: @or_fcmp_redundant_and_neg2(
128+
define i1 @or_fcmp_redundant_and_6(double %v0) {
129+
; CHECK-LABEL: @or_fcmp_redundant_and_6(
133130
; CHECK-NEXT: [[V1:%.*]] = fcmp ogt double [[V0:%.*]], 1.000000e-02
134131
; CHECK-NEXT: [[V2:%.*]] = fcmp olt double [[V0]], 1.990000e+00
135132
; CHECK-NEXT: [[V3:%.*]] = and i1 [[V1]], [[V2]]

0 commit comments

Comments
 (0)