Skip to content

Commit d55692d

Browse files
committed
Reapply "ValueTracking: Identify implied fp classes by general fcmp (#66505)"
This reverts commit 96a0d71. Avoid assert with dynamic denormal-fp-math We don't recognize compares with 0 as an exact class test if we don't know the denormal mode. We could try to do better here, but it's probably not worth it. Fixes asserts reported after 1adce7d8e47e2438f99f91607760b825e5e3cc37
1 parent 2c976a1 commit d55692d

File tree

4 files changed

+500
-422
lines changed

4 files changed

+500
-422
lines changed

llvm/include/llvm/Analysis/ValueTracking.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,27 @@ std::pair<Value *, FPClassTest> fcmpToClassTest(CmpInst::Predicate Pred,
214214
const APFloat *ConstRHS,
215215
bool LookThroughSrc = true);
216216

217+
/// Compute the possible floating-point classes that \p LHS could be based on an
218+
/// fcmp returning true. Returns { TestedValue, ClassesIfTrue, ClassesIfFalse }
219+
///
220+
/// If the compare returns an exact class test, ClassesIfTrue == ~ClassesIfFalse
221+
///
222+
/// This is a less exact version of fcmpToClassTest (e.g. fcmpToClassTest will
223+
/// only succeed for a test of x > 0 implies positive, but not x > 1).
224+
///
225+
/// If \p LookThroughSrc is true, consider the input value when computing the
226+
/// mask. This may look through sign bit operations.
227+
///
228+
/// If \p LookThroughSrc is false, ignore the source value (i.e. the first pair
229+
/// element will always be LHS.
230+
///
231+
std::tuple<Value *, FPClassTest, FPClassTest>
232+
fcmpImpliesClass(CmpInst::Predicate Pred, const Function &F, Value *LHS,
233+
const APFloat *ConstRHS, bool LookThroughSrc = true);
234+
std::tuple<Value *, FPClassTest, FPClassTest>
235+
fcmpImpliesClass(CmpInst::Predicate Pred, const Function &F, Value *LHS,
236+
Value *RHS, bool LookThroughSrc = true);
237+
217238
struct KnownFPClass {
218239
/// Floating-point classes the value could be one of.
219240
FPClassTest KnownFPClasses = fcAllFlags;

llvm/lib/Analysis/ValueTracking.cpp

Lines changed: 166 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4164,6 +4164,147 @@ llvm::fcmpToClassTest(FCmpInst::Predicate Pred, const Function &F, Value *LHS,
41644164
return {Src, Mask};
41654165
}
41664166

4167+
std::tuple<Value *, FPClassTest, FPClassTest>
4168+
llvm::fcmpImpliesClass(CmpInst::Predicate Pred, const Function &F, Value *LHS,
4169+
const APFloat *ConstRHS, bool LookThroughSrc) {
4170+
auto [Val, ClassMask] =
4171+
fcmpToClassTest(Pred, F, LHS, ConstRHS, LookThroughSrc);
4172+
if (Val)
4173+
return {Val, ClassMask, ~ClassMask};
4174+
4175+
FPClassTest RHSClass = ConstRHS->classify();
4176+
4177+
// If we see a zero here, we are using dynamic denormal-fp-math, and can't
4178+
// treat comparisons to 0 as an exact class test.
4179+
//
4180+
// TODO: We could do better and still recognize non-equality cases.
4181+
if (RHSClass == fcPosZero || RHSClass == fcNegZero)
4182+
return {nullptr, fcAllFlags, fcAllFlags};
4183+
4184+
assert((RHSClass == fcPosNormal || RHSClass == fcNegNormal ||
4185+
RHSClass == fcPosSubnormal || RHSClass == fcNegSubnormal) &&
4186+
"should have been recognized as an exact class test");
4187+
4188+
const bool IsNegativeRHS = (RHSClass & fcNegative) == RHSClass;
4189+
const bool IsPositiveRHS = (RHSClass & fcPositive) == RHSClass;
4190+
4191+
assert(IsNegativeRHS == ConstRHS->isNegative());
4192+
assert(IsPositiveRHS == !ConstRHS->isNegative());
4193+
4194+
Value *Src = LHS;
4195+
const bool IsFabs = LookThroughSrc && match(LHS, m_FAbs(m_Value(Src)));
4196+
4197+
if (IsFabs)
4198+
RHSClass = llvm::inverse_fabs(RHSClass);
4199+
4200+
if (Pred == FCmpInst::FCMP_OEQ)
4201+
return {Src, RHSClass, fcAllFlags};
4202+
4203+
if (Pred == FCmpInst::FCMP_UEQ) {
4204+
FPClassTest Class = RHSClass | fcNan;
4205+
return {Src, Class, ~fcNan};
4206+
}
4207+
4208+
if (Pred == FCmpInst::FCMP_ONE)
4209+
return {Src, ~fcNan, RHSClass};
4210+
4211+
if (Pred == FCmpInst::FCMP_UNE)
4212+
return {Src, fcAllFlags, RHSClass};
4213+
4214+
if (IsNegativeRHS) {
4215+
// TODO: Handle fneg(fabs)
4216+
if (IsFabs) {
4217+
// fabs(x) o> -k -> fcmp ord x, x
4218+
// fabs(x) u> -k -> true
4219+
// fabs(x) o< -k -> false
4220+
// fabs(x) u< -k -> fcmp uno x, x
4221+
switch (Pred) {
4222+
case FCmpInst::FCMP_OGT:
4223+
case FCmpInst::FCMP_OGE:
4224+
return {Src, ~fcNan, fcNan};
4225+
case FCmpInst::FCMP_UGT:
4226+
case FCmpInst::FCMP_UGE:
4227+
return {Src, fcAllFlags, fcNone};
4228+
case FCmpInst::FCMP_OLT:
4229+
case FCmpInst::FCMP_OLE:
4230+
return {Src, fcNone, fcAllFlags};
4231+
case FCmpInst::FCMP_ULT:
4232+
case FCmpInst::FCMP_ULE:
4233+
return {Src, fcNan, ~fcNan};
4234+
default:
4235+
break;
4236+
}
4237+
4238+
return {nullptr, fcAllFlags, fcAllFlags};
4239+
}
4240+
4241+
FPClassTest ClassesLE = fcNegInf | fcNegNormal;
4242+
FPClassTest ClassesGE = fcPositive | fcNegZero | fcNegSubnormal;
4243+
4244+
if (ConstRHS->isDenormal())
4245+
ClassesLE |= fcNegSubnormal;
4246+
else
4247+
ClassesGE |= fcNegNormal;
4248+
4249+
switch (Pred) {
4250+
case FCmpInst::FCMP_OGT:
4251+
case FCmpInst::FCMP_OGE:
4252+
return {Src, ClassesGE, ~ClassesGE | RHSClass};
4253+
case FCmpInst::FCMP_UGT:
4254+
case FCmpInst::FCMP_UGE:
4255+
return {Src, ClassesGE | fcNan, ~(ClassesGE | fcNan) | RHSClass};
4256+
case FCmpInst::FCMP_OLT:
4257+
case FCmpInst::FCMP_OLE:
4258+
return {Src, ClassesLE, ~ClassesLE | RHSClass};
4259+
case FCmpInst::FCMP_ULT:
4260+
case FCmpInst::FCMP_ULE:
4261+
return {Src, ClassesLE | fcNan, ~(ClassesLE | fcNan) | RHSClass};
4262+
default:
4263+
break;
4264+
}
4265+
} else if (IsPositiveRHS) {
4266+
FPClassTest ClassesGE = fcPosNormal | fcPosInf;
4267+
FPClassTest ClassesLE = fcNegative | fcPosZero | fcPosNormal;
4268+
if (ConstRHS->isDenormal())
4269+
ClassesGE |= fcPosNormal;
4270+
else
4271+
ClassesLE |= fcPosSubnormal;
4272+
4273+
if (IsFabs) {
4274+
ClassesGE = llvm::inverse_fabs(ClassesGE);
4275+
ClassesLE = llvm::inverse_fabs(ClassesLE);
4276+
}
4277+
4278+
switch (Pred) {
4279+
case FCmpInst::FCMP_OGT:
4280+
case FCmpInst::FCMP_OGE:
4281+
return {Src, ClassesGE, ~ClassesGE | RHSClass};
4282+
case FCmpInst::FCMP_UGT:
4283+
case FCmpInst::FCMP_UGE:
4284+
return {Src, ClassesGE | fcNan, ~(ClassesGE | fcNan) | RHSClass};
4285+
case FCmpInst::FCMP_OLT:
4286+
case FCmpInst::FCMP_OLE:
4287+
return {Src, ClassesLE, ~ClassesLE | RHSClass};
4288+
case FCmpInst::FCMP_ULT:
4289+
case FCmpInst::FCMP_ULE:
4290+
return {Src, ClassesLE | fcNan, ~(ClassesLE | fcNan) | RHSClass};
4291+
default:
4292+
break;
4293+
}
4294+
}
4295+
4296+
return {nullptr, fcAllFlags, fcAllFlags};
4297+
}
4298+
4299+
std::tuple<Value *, FPClassTest, FPClassTest>
4300+
llvm::fcmpImpliesClass(CmpInst::Predicate Pred, const Function &F, Value *LHS,
4301+
Value *RHS, bool LookThroughSrc) {
4302+
const APFloat *ConstRHS;
4303+
if (!match(RHS, m_APFloatAllowUndef(ConstRHS)))
4304+
return {nullptr, fcAllFlags, fcNone};
4305+
return fcmpImpliesClass(Pred, F, LHS, ConstRHS, LookThroughSrc);
4306+
}
4307+
41674308
static FPClassTest computeKnownFPClassFromAssumes(const Value *V,
41684309
const SimplifyQuery &Q) {
41694310
FPClassTest KnownFromAssume = fcAllFlags;
@@ -4188,18 +4329,21 @@ static FPClassTest computeKnownFPClassFromAssumes(const Value *V,
41884329
Value *LHS, *RHS;
41894330
uint64_t ClassVal = 0;
41904331
if (match(I->getArgOperand(0), m_FCmp(Pred, m_Value(LHS), m_Value(RHS)))) {
4191-
auto [TestedValue, TestedMask] =
4192-
fcmpToClassTest(Pred, *F, LHS, RHS, true);
4193-
// First see if we can fold in fabs/fneg into the test.
4194-
if (TestedValue == V)
4195-
KnownFromAssume &= TestedMask;
4196-
else {
4197-
// Try again without the lookthrough if we found a different source
4198-
// value.
4199-
auto [TestedValue, TestedMask] =
4200-
fcmpToClassTest(Pred, *F, LHS, RHS, false);
4201-
if (TestedValue == V)
4202-
KnownFromAssume &= TestedMask;
4332+
const APFloat *CRHS;
4333+
if (match(RHS, m_APFloat(CRHS))) {
4334+
// First see if we can fold in fabs/fneg into the test.
4335+
auto [CmpVal, MaskIfTrue, MaskIfFalse] =
4336+
fcmpImpliesClass(Pred, *F, LHS, CRHS, true);
4337+
if (CmpVal == V)
4338+
KnownFromAssume &= MaskIfTrue;
4339+
else {
4340+
// Try again without the lookthrough if we found a different source
4341+
// value.
4342+
auto [CmpVal, MaskIfTrue, MaskIfFalse] =
4343+
fcmpImpliesClass(Pred, *F, LHS, CRHS, false);
4344+
if (CmpVal == V)
4345+
KnownFromAssume &= MaskIfTrue;
4346+
}
42034347
}
42044348
} else if (match(I->getArgOperand(0),
42054349
m_Intrinsic<Intrinsic::is_fpclass>(
@@ -4347,7 +4491,8 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
43474491
FPClassTest FilterRHS = fcAllFlags;
43484492

43494493
Value *TestedValue = nullptr;
4350-
FPClassTest TestedMask = fcNone;
4494+
FPClassTest MaskIfTrue = fcAllFlags;
4495+
FPClassTest MaskIfFalse = fcAllFlags;
43514496
uint64_t ClassVal = 0;
43524497
const Function *F = cast<Instruction>(Op)->getFunction();
43534498
CmpInst::Predicate Pred;
@@ -4359,20 +4504,22 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
43594504
// TODO: In some degenerate cases we can infer something if we try again
43604505
// without looking through sign operations.
43614506
bool LookThroughFAbsFNeg = CmpLHS != LHS && CmpLHS != RHS;
4362-
std::tie(TestedValue, TestedMask) =
4363-
fcmpToClassTest(Pred, *F, CmpLHS, CmpRHS, LookThroughFAbsFNeg);
4507+
std::tie(TestedValue, MaskIfTrue, MaskIfFalse) =
4508+
fcmpImpliesClass(Pred, *F, CmpLHS, CmpRHS, LookThroughFAbsFNeg);
43644509
} else if (match(Cond,
43654510
m_Intrinsic<Intrinsic::is_fpclass>(
43664511
m_Value(TestedValue), m_ConstantInt(ClassVal)))) {
4367-
TestedMask = static_cast<FPClassTest>(ClassVal);
4512+
FPClassTest TestedMask = static_cast<FPClassTest>(ClassVal);
4513+
MaskIfTrue = TestedMask;
4514+
MaskIfFalse = ~TestedMask;
43684515
}
43694516

43704517
if (TestedValue == LHS) {
43714518
// match !isnan(x) ? x : y
4372-
FilterLHS = TestedMask;
4373-
} else if (TestedValue == RHS) {
4519+
FilterLHS = MaskIfTrue;
4520+
} else if (TestedValue == RHS) { // && IsExactClass
43744521
// match !isnan(x) ? y : x
4375-
FilterRHS = ~TestedMask;
4522+
FilterRHS = MaskIfFalse;
43764523
}
43774524

43784525
KnownFPClass Known2;

0 commit comments

Comments
 (0)