Skip to content

Commit 0375094

Browse files
committed
ValueTracking: Identify implied fp classes by general fcmp
Previously we could recognize exact class tests performed by an fcmp with special values (0s, infs and smallest normal). Expand this to recognize the implied classes by a compare with a general constant. e.g. fcmp ogt x, 1 implies positive and non-0. The API should be better merged with fcmpToClassTest but that made the diff way bigger, will try to do that in a future patch.
1 parent 3cef582 commit 0375094

File tree

4 files changed

+493
-422
lines changed

4 files changed

+493
-422
lines changed

llvm/include/llvm/Analysis/ValueTracking.h

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

243+
/// Compute the possible floating-point classes that \p LHS could be based on an
244+
/// fcmp returning true. Returns { TestedValue, ClassesIfTrue, ClassesIfFalse }
245+
///
246+
/// If the compare returns an exact class test, ClassesIfTrue == ~ClassesIfFalse
247+
///
248+
/// This is a less exact version of fcmpToClassTest (e.g. fcmpToClassTest will
249+
/// only succeed for a test of x > 0 implies positive, but not x > 1).
250+
///
251+
/// If \p LookThroughSrc is true, consider the input value when computing the
252+
/// mask. This may look through sign bit operations.
253+
///
254+
/// If \p LookThroughSrc is false, ignore the source value (i.e. the first pair
255+
/// element will always be LHS.
256+
///
257+
std::tuple<Value *, FPClassTest, FPClassTest>
258+
fcmpImpliesClass(CmpInst::Predicate Pred, const Function &F, Value *LHS,
259+
const APFloat *ConstRHS, bool LookThroughSrc = true);
260+
std::tuple<Value *, FPClassTest, FPClassTest>
261+
fcmpImpliesClass(CmpInst::Predicate Pred, const Function &F, Value *LHS,
262+
Value *RHS, bool LookThroughSrc = true);
263+
243264
struct KnownFPClass {
244265
/// Floating-point classes the value could be one of.
245266
FPClassTest KnownFPClasses = fcAllFlags;

llvm/lib/Analysis/ValueTracking.cpp

Lines changed: 159 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4245,6 +4245,140 @@ llvm::fcmpToClassTest(FCmpInst::Predicate Pred, const Function &F, Value *LHS,
42454245
return {Src, Mask};
42464246
}
42474247

4248+
std::tuple<Value *, FPClassTest, FPClassTest>
4249+
llvm::fcmpImpliesClass(CmpInst::Predicate Pred, const Function &F, Value *LHS,
4250+
const APFloat *ConstRHS, bool LookThroughSrc) {
4251+
auto [Val, ClassMask] =
4252+
fcmpToClassTest(Pred, F, LHS, ConstRHS, LookThroughSrc);
4253+
if (Val)
4254+
return {Val, ClassMask, ~ClassMask};
4255+
4256+
FPClassTest RHSClass = ConstRHS->classify();
4257+
assert((RHSClass == fcPosNormal || RHSClass == fcNegNormal ||
4258+
RHSClass == fcPosSubnormal || RHSClass == fcNegSubnormal) &&
4259+
"should have been recognized as an exact class test");
4260+
4261+
const bool IsNegativeRHS = (RHSClass & fcNegative) == RHSClass;
4262+
const bool IsPositiveRHS = (RHSClass & fcPositive) == RHSClass;
4263+
4264+
assert(IsNegativeRHS == ConstRHS->isNegative());
4265+
assert(IsPositiveRHS == !ConstRHS->isNegative());
4266+
4267+
Value *Src = LHS;
4268+
const bool IsFabs = LookThroughSrc && match(LHS, m_FAbs(m_Value(Src)));
4269+
4270+
if (IsFabs)
4271+
RHSClass = llvm::inverse_fabs(RHSClass);
4272+
4273+
if (Pred == FCmpInst::FCMP_OEQ)
4274+
return {Src, RHSClass, fcAllFlags};
4275+
4276+
if (Pred == FCmpInst::FCMP_UEQ) {
4277+
FPClassTest Class = RHSClass | fcNan;
4278+
return {Src, Class, ~fcNan};
4279+
}
4280+
4281+
if (Pred == FCmpInst::FCMP_ONE)
4282+
return {Src, ~fcNan, RHSClass};
4283+
4284+
if (Pred == FCmpInst::FCMP_UNE)
4285+
return {Src, fcAllFlags, RHSClass};
4286+
4287+
if (IsNegativeRHS) {
4288+
// TODO: Handle fneg(fabs)
4289+
if (IsFabs) {
4290+
// fabs(x) o> -k -> fcmp ord x, x
4291+
// fabs(x) u> -k -> true
4292+
// fabs(x) o< -k -> false
4293+
// fabs(x) u< -k -> fcmp uno x, x
4294+
switch (Pred) {
4295+
case FCmpInst::FCMP_OGT:
4296+
case FCmpInst::FCMP_OGE:
4297+
return {Src, ~fcNan, fcNan};
4298+
case FCmpInst::FCMP_UGT:
4299+
case FCmpInst::FCMP_UGE:
4300+
return {Src, fcAllFlags, fcNone};
4301+
case FCmpInst::FCMP_OLT:
4302+
case FCmpInst::FCMP_OLE:
4303+
return {Src, fcNone, fcAllFlags};
4304+
case FCmpInst::FCMP_ULT:
4305+
case FCmpInst::FCMP_ULE:
4306+
return {Src, fcNan, ~fcNan};
4307+
default:
4308+
break;
4309+
}
4310+
4311+
return {nullptr, fcAllFlags, fcAllFlags};
4312+
}
4313+
4314+
FPClassTest ClassesLE = fcNegInf | fcNegNormal;
4315+
FPClassTest ClassesGE = fcPositive | fcNegZero | fcNegSubnormal;
4316+
4317+
if (ConstRHS->isDenormal())
4318+
ClassesLE |= fcNegSubnormal;
4319+
else
4320+
ClassesGE |= fcNegNormal;
4321+
4322+
switch (Pred) {
4323+
case FCmpInst::FCMP_OGT:
4324+
case FCmpInst::FCMP_OGE:
4325+
return {Src, ClassesGE, ~ClassesGE | RHSClass};
4326+
case FCmpInst::FCMP_UGT:
4327+
case FCmpInst::FCMP_UGE:
4328+
return {Src, ClassesGE | fcNan, ~(ClassesGE | fcNan) | RHSClass};
4329+
case FCmpInst::FCMP_OLT:
4330+
case FCmpInst::FCMP_OLE:
4331+
return {Src, ClassesLE, ~ClassesLE | RHSClass};
4332+
case FCmpInst::FCMP_ULT:
4333+
case FCmpInst::FCMP_ULE:
4334+
return {Src, ClassesLE | fcNan, ~(ClassesLE | fcNan) | RHSClass};
4335+
default:
4336+
break;
4337+
}
4338+
} else if (IsPositiveRHS) {
4339+
FPClassTest ClassesGE = fcPosNormal | fcPosInf;
4340+
FPClassTest ClassesLE = fcNegative | fcPosZero | fcPosNormal;
4341+
if (ConstRHS->isDenormal())
4342+
ClassesGE |= fcPosNormal;
4343+
else
4344+
ClassesLE |= fcPosSubnormal;
4345+
4346+
FPClassTest FalseClasses = RHSClass;
4347+
if (IsFabs) {
4348+
ClassesGE = llvm::inverse_fabs(ClassesGE);
4349+
ClassesLE = llvm::inverse_fabs(ClassesLE);
4350+
}
4351+
4352+
switch (Pred) {
4353+
case FCmpInst::FCMP_OGT:
4354+
case FCmpInst::FCMP_OGE:
4355+
return {Src, ClassesGE, ~ClassesGE | FalseClasses};
4356+
case FCmpInst::FCMP_UGT:
4357+
case FCmpInst::FCMP_UGE:
4358+
return {Src, ClassesGE | fcNan, ~(ClassesGE | fcNan) | FalseClasses};
4359+
case FCmpInst::FCMP_OLT:
4360+
case FCmpInst::FCMP_OLE:
4361+
return {Src, ClassesLE, ~ClassesLE | FalseClasses};
4362+
case FCmpInst::FCMP_ULT:
4363+
case FCmpInst::FCMP_ULE:
4364+
return {Src, ClassesLE | fcNan, ~(ClassesLE | fcNan) | FalseClasses};
4365+
default:
4366+
break;
4367+
}
4368+
}
4369+
4370+
return {nullptr, fcAllFlags, fcAllFlags};
4371+
}
4372+
4373+
std::tuple<Value *, FPClassTest, FPClassTest>
4374+
llvm::fcmpImpliesClass(CmpInst::Predicate Pred, const Function &F, Value *LHS,
4375+
Value *RHS, bool LookThroughSrc) {
4376+
const APFloat *ConstRHS;
4377+
if (!match(RHS, m_APFloatAllowUndef(ConstRHS)))
4378+
return {nullptr, fcAllFlags, fcNone};
4379+
return fcmpImpliesClass(Pred, F, LHS, ConstRHS, LookThroughSrc);
4380+
}
4381+
42484382
static FPClassTest computeKnownFPClassFromAssumes(const Value *V,
42494383
const SimplifyQuery &Q) {
42504384
FPClassTest KnownFromAssume = fcAllFlags;
@@ -4269,18 +4403,21 @@ static FPClassTest computeKnownFPClassFromAssumes(const Value *V,
42694403
Value *LHS, *RHS;
42704404
uint64_t ClassVal = 0;
42714405
if (match(I->getArgOperand(0), m_FCmp(Pred, m_Value(LHS), m_Value(RHS)))) {
4272-
auto [TestedValue, TestedMask] =
4273-
fcmpToClassTest(Pred, *F, LHS, RHS, true);
4274-
// First see if we can fold in fabs/fneg into the test.
4275-
if (TestedValue == V)
4276-
KnownFromAssume &= TestedMask;
4277-
else {
4278-
// Try again without the lookthrough if we found a different source
4279-
// value.
4280-
auto [TestedValue, TestedMask] =
4281-
fcmpToClassTest(Pred, *F, LHS, RHS, false);
4282-
if (TestedValue == V)
4283-
KnownFromAssume &= TestedMask;
4406+
const APFloat *CRHS;
4407+
if (match(RHS, m_APFloat(CRHS))) {
4408+
// First see if we can fold in fabs/fneg into the test.
4409+
auto [CmpVal, MaskIfTrue, MaskIfFalse] =
4410+
fcmpImpliesClass(Pred, *F, LHS, CRHS, true);
4411+
if (CmpVal == V)
4412+
KnownFromAssume &= MaskIfTrue;
4413+
else {
4414+
// Try again without the lookthrough if we found a different source
4415+
// value.
4416+
auto [CmpVal, MaskIfTrue, MaskIfFalse] =
4417+
fcmpImpliesClass(Pred, *F, LHS, CRHS, false);
4418+
if (CmpVal == V)
4419+
KnownFromAssume &= MaskIfTrue;
4420+
}
42844421
}
42854422
} else if (match(I->getArgOperand(0),
42864423
m_Intrinsic<Intrinsic::is_fpclass>(
@@ -4428,7 +4565,8 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
44284565
FPClassTest FilterRHS = fcAllFlags;
44294566

44304567
Value *TestedValue = nullptr;
4431-
FPClassTest TestedMask = fcNone;
4568+
FPClassTest MaskIfTrue = fcAllFlags;
4569+
FPClassTest MaskIfFalse = fcAllFlags;
44324570
uint64_t ClassVal = 0;
44334571
const Function *F = cast<Instruction>(Op)->getFunction();
44344572
CmpInst::Predicate Pred;
@@ -4440,20 +4578,22 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
44404578
// TODO: In some degenerate cases we can infer something if we try again
44414579
// without looking through sign operations.
44424580
bool LookThroughFAbsFNeg = CmpLHS != LHS && CmpLHS != RHS;
4443-
std::tie(TestedValue, TestedMask) =
4444-
fcmpToClassTest(Pred, *F, CmpLHS, CmpRHS, LookThroughFAbsFNeg);
4581+
std::tie(TestedValue, MaskIfTrue, MaskIfFalse) =
4582+
fcmpImpliesClass(Pred, *F, CmpLHS, CmpRHS, LookThroughFAbsFNeg);
44454583
} else if (match(Cond,
44464584
m_Intrinsic<Intrinsic::is_fpclass>(
44474585
m_Value(TestedValue), m_ConstantInt(ClassVal)))) {
4448-
TestedMask = static_cast<FPClassTest>(ClassVal);
4586+
FPClassTest TestedMask = static_cast<FPClassTest>(ClassVal);
4587+
MaskIfTrue = TestedMask;
4588+
MaskIfFalse = ~TestedMask;
44494589
}
44504590

44514591
if (TestedValue == LHS) {
44524592
// match !isnan(x) ? x : y
4453-
FilterLHS = TestedMask;
4454-
} else if (TestedValue == RHS) {
4593+
FilterLHS = MaskIfTrue;
4594+
} else if (TestedValue == RHS) { // && IsExactClass
44554595
// match !isnan(x) ? y : x
4456-
FilterRHS = ~TestedMask;
4596+
FilterRHS = MaskIfFalse;
44574597
}
44584598

44594599
KnownFPClass Known2;

0 commit comments

Comments
 (0)