Skip to content

Commit a46422a

Browse files
committed
Reapply "ValueTracking: Identify implied fp classes by general fcmp (#66505)"
This reverts commit 0d0c229. Includes a bug fix for fcmp one handling, as well as for positive constants.
1 parent e390c22 commit a46422a

File tree

4 files changed

+522
-423
lines changed

4 files changed

+522
-423
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: 159 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4230,6 +4230,8 @@ llvm::fcmpToClassTest(FCmpInst::Predicate Pred, const Function &F, Value *LHS,
42304230
default:
42314231
return {nullptr, fcAllFlags};
42324232
}
4233+
4234+
// TODO: Handle ole largest denormal
42334235
} else if (ConstRHS->isNaN()) {
42344236
// fcmp o__ x, nan -> false
42354237
// fcmp u__ x, nan -> true
@@ -4244,6 +4246,147 @@ llvm::fcmpToClassTest(FCmpInst::Predicate Pred, const Function &F, Value *LHS,
42444246
return {Src, Mask};
42454247
}
42464248

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

44294566
Value *TestedValue = nullptr;
4430-
FPClassTest TestedMask = fcNone;
4567+
FPClassTest MaskIfTrue = fcAllFlags;
4568+
FPClassTest MaskIfFalse = fcAllFlags;
44314569
uint64_t ClassVal = 0;
44324570
const Function *F = cast<Instruction>(Op)->getFunction();
44334571
CmpInst::Predicate Pred;
@@ -4439,20 +4577,22 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
44394577
// TODO: In some degenerate cases we can infer something if we try again
44404578
// without looking through sign operations.
44414579
bool LookThroughFAbsFNeg = CmpLHS != LHS && CmpLHS != RHS;
4442-
std::tie(TestedValue, TestedMask) =
4443-
fcmpToClassTest(Pred, *F, CmpLHS, CmpRHS, LookThroughFAbsFNeg);
4580+
std::tie(TestedValue, MaskIfTrue, MaskIfFalse) =
4581+
fcmpImpliesClass(Pred, *F, CmpLHS, CmpRHS, LookThroughFAbsFNeg);
44444582
} else if (match(Cond,
44454583
m_Intrinsic<Intrinsic::is_fpclass>(
44464584
m_Value(TestedValue), m_ConstantInt(ClassVal)))) {
4447-
TestedMask = static_cast<FPClassTest>(ClassVal);
4585+
FPClassTest TestedMask = static_cast<FPClassTest>(ClassVal);
4586+
MaskIfTrue = TestedMask;
4587+
MaskIfFalse = ~TestedMask;
44484588
}
44494589

44504590
if (TestedValue == LHS) {
44514591
// match !isnan(x) ? x : y
4452-
FilterLHS = TestedMask;
4453-
} else if (TestedValue == RHS) {
4592+
FilterLHS = MaskIfTrue;
4593+
} else if (TestedValue == RHS) { // && IsExactClass
44544594
// match !isnan(x) ? y : x
4455-
FilterRHS = ~TestedMask;
4595+
FilterRHS = MaskIfFalse;
44564596
}
44574597

44584598
KnownFPClass Known2;

0 commit comments

Comments
 (0)