[llvm] d2404ea - Attributor: Assume handling for nofpclass
Matt Arsenault via llvm-commits
llvm-commits at lists.llvm.org
Fri Mar 17 04:39:55 PDT 2023
Author: Matt Arsenault
Date: 2023-03-17T07:39:40-04:00
New Revision: d2404ea6ced5fce9442260bde08a02d607fdd50d
URL: https://github.com/llvm/llvm-project/commit/d2404ea6ced5fce9442260bde08a02d607fdd50d
DIFF: https://github.com/llvm/llvm-project/commit/d2404ea6ced5fce9442260bde08a02d607fdd50d.diff
LOG: Attributor: Assume handling for nofpclass
Added:
Modified:
llvm/include/llvm/Analysis/ValueTracking.h
llvm/lib/Analysis/AssumptionCache.cpp
llvm/lib/Analysis/ValueTracking.cpp
llvm/lib/Transforms/IPO/AttributorAttributes.cpp
llvm/test/Transforms/Attributor/nofpclass.ll
Removed:
################################################################################
diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index 0a8af8c44999..51ee3ad27fbf 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -219,9 +219,16 @@ Intrinsic::ID getIntrinsicForCallSite(const CallBase &CB,
/// Returns a pair of values, which if passed to llvm.is.fpclass, returns the
/// same result as an fcmp with the given operands.
+///
+/// If \p LookThroughSrc is true, consider the input value when computing the
+/// mask.
+///
+/// If \p LookThroughSrc is false, ignore the source value (i.e. the first pair
+/// element will always be LHS.
std::pair<Value *, FPClassTest> fcmpToClassTest(CmpInst::Predicate Pred,
- const Function &F,
- Value *LHS, Value *RHS);
+ const Function &F, Value *LHS,
+ Value *RHS,
+ bool LookThroughSrc = true);
struct KnownFPClass {
/// Floating-point classes the value could be one of.
diff --git a/llvm/lib/Analysis/AssumptionCache.cpp b/llvm/lib/Analysis/AssumptionCache.cpp
index 11796ef50444..b439dc1e6a76 100644
--- a/llvm/lib/Analysis/AssumptionCache.cpp
+++ b/llvm/lib/Analysis/AssumptionCache.cpp
@@ -87,7 +87,7 @@ findAffectedValues(CallBase *CI, TargetTransformInfo *TTI,
AddAffected(Cond);
CmpInst::Predicate Pred;
- if (match(Cond, m_ICmp(Pred, m_Value(A), m_Value(B)))) {
+ if (match(Cond, m_Cmp(Pred, m_Value(A), m_Value(B)))) {
AddAffected(A);
AddAffected(B);
@@ -128,7 +128,18 @@ findAffectedValues(CallBase *CI, TargetTransformInfo *TTI,
if (match(A, m_Add(m_Value(X), m_ConstantInt())) &&
match(B, m_ConstantInt()))
AddAffected(X);
+ } else if (CmpInst::isFPPredicate(Pred)) {
+ // fcmp fneg(x), y
+ // fcmp fabs(x), y
+ // fcmp fneg(fabs(x)), y
+ if (match(A, m_FNeg(m_Value(A))))
+ AddAffected(A);
+ if (match(A, m_FAbs(m_Value(A))))
+ AddAffected(A);
}
+ } else if (match(Cond, m_Intrinsic<Intrinsic::is_fpclass>(m_Value(A),
+ m_Value(B)))) {
+ AddAffected(A);
}
if (TTI) {
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 1ce06e5b57d8..905b4ac6174a 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -4134,7 +4134,8 @@ static bool inputDenormalIsIEEE(const Function &F, const Value *Val) {
/// same result as an fcmp with the given operands.
std::pair<Value *, FPClassTest> llvm::fcmpToClassTest(FCmpInst::Predicate Pred,
const Function &F,
- Value *LHS, Value *RHS) {
+ Value *LHS, Value *RHS,
+ bool LookThroughSrc) {
const APFloat *ConstRHS;
if (!match(RHS, m_APFloat(ConstRHS)))
return {nullptr, fcNone};
@@ -4168,7 +4169,7 @@ std::pair<Value *, FPClassTest> llvm::fcmpToClassTest(FCmpInst::Predicate Pred,
}
Value *Src = LHS;
- const bool IsFabs = match(LHS, m_FAbs(m_Value(Src)));
+ const bool IsFabs = LookThroughSrc && match(LHS, m_FAbs(m_Value(Src)));
// Compute the test mask that would return true for the ordered comparisons.
FPClassTest Mask;
@@ -4295,6 +4296,53 @@ std::pair<Value *, FPClassTest> llvm::fcmpToClassTest(FCmpInst::Predicate Pred,
return {Src, Mask};
}
+static FPClassTest computeKnownFPClassFromAssumes(const Value *V,
+ const Query &Q) {
+ FPClassTest KnownFromAssume = fcNone;
+
+ // Try to restrict the floating-point classes based on information from
+ // assumptions.
+ for (auto &AssumeVH : Q.AC->assumptionsFor(V)) {
+ if (!AssumeVH)
+ continue;
+ CallInst *I = cast<CallInst>(AssumeVH);
+ const Function *F = I->getFunction();
+
+ assert(F == Q.CxtI->getParent()->getParent() &&
+ "Got assumption for the wrong function!");
+ assert(I->getCalledFunction()->getIntrinsicID() == Intrinsic::assume &&
+ "must be an assume intrinsic");
+
+ if (!isValidAssumeForContext(I, Q.CxtI, Q.DT))
+ continue;
+
+ CmpInst::Predicate Pred;
+ Value *LHS, *RHS;
+ uint64_t ClassVal = 0;
+ if (match(I->getArgOperand(0), m_FCmp(Pred, m_Value(LHS), m_Value(RHS)))) {
+ auto [TestedValue, TestedMask] =
+ fcmpToClassTest(Pred, *F, LHS, RHS, true);
+ // First see if we can fold in fabs/fneg into the test.
+ if (TestedValue == V)
+ KnownFromAssume |= TestedMask;
+ else {
+ // Try again without the lookthrough if we found a
diff erent source
+ // value.
+ auto [TestedValue, TestedMask] =
+ fcmpToClassTest(Pred, *F, LHS, RHS, false);
+ if (TestedValue == V)
+ KnownFromAssume |= TestedMask;
+ }
+ } else if (match(I->getArgOperand(0),
+ m_Intrinsic<Intrinsic::is_fpclass>(
+ m_Value(LHS), m_ConstantInt(ClassVal)))) {
+ KnownFromAssume |= static_cast<FPClassTest>(ClassVal);
+ }
+ }
+
+ return KnownFromAssume;
+}
+
// TODO: Merge implementations of isKnownNeverNaN, isKnownNeverInfinity,
// CannotBeNegativeZero, cannotBeOrderedLessThanZero into here.
void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
@@ -4332,6 +4380,9 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
KnownNotFromFlags |= fcInf;
}
+ if (Q.AC)
+ KnownNotFromFlags |= computeKnownFPClassFromAssumes(V, Q);
+
// We no longer need to find out about these bits from inputs if we can
// assume this from flags/attributes.
InterestedClasses &= ~KnownNotFromFlags;
diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
index d8a0a3c85628..a8067743fbd1 100644
--- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
+++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
@@ -10232,6 +10232,9 @@ struct AANoFPClassImpl : AANoFPClass {
KnownFPClass KnownFPClass = computeKnownFPClass(&V, DL);
addKnownBits(~KnownFPClass.KnownFPClasses);
}
+
+ if (Instruction *CtxI = getCtxI())
+ followUsesInMBEC(*this, A, getState(), *CtxI);
}
/// See followUsesInMBEC
diff --git a/llvm/test/Transforms/Attributor/nofpclass.ll b/llvm/test/Transforms/Attributor/nofpclass.ll
index 5b2d7ddeee90..78ba8d678de8 100644
--- a/llvm/test/Transforms/Attributor/nofpclass.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass.ll
@@ -306,12 +306,12 @@ bb1:
; Should be able to infer nofpclass on both %arg uses
define float @fcmp_ord_assume_callsite_arg_return(float %arg) {
-; CHECK-LABEL: define float @fcmp_ord_assume_callsite_arg_return
-; CHECK-SAME: (float returned [[ARG:%.*]]) {
+; CHECK-LABEL: define nofpclass(inf zero sub norm) float @fcmp_ord_assume_callsite_arg_return
+; CHECK-SAME: (float returned nofpclass(inf zero sub norm) [[ARG:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[IS_NOT_NAN:%.*]] = fcmp ord float [[ARG]], 0.000000e+00
; CHECK-NEXT: call void @llvm.assume(i1 noundef [[IS_NOT_NAN]]) #[[ATTR6:[0-9]+]]
-; CHECK-NEXT: call void @extern.use(float [[ARG]])
+; CHECK-NEXT: call void @extern.use(float nofpclass(inf zero sub norm) [[ARG]])
; CHECK-NEXT: ret float [[ARG]]
;
entry:
@@ -431,12 +431,12 @@ define float @call_noinf_return_1(float %arg) {
}
define float @fcmp_olt_assume_one_0_callsite_arg_return(float %arg) {
-; CHECK-LABEL: define float @fcmp_olt_assume_one_0_callsite_arg_return
-; CHECK-SAME: (float returned [[ARG:%.*]]) {
+; CHECK-LABEL: define nofpclass(inf sub norm) float @fcmp_olt_assume_one_0_callsite_arg_return
+; CHECK-SAME: (float returned nofpclass(inf sub norm) [[ARG:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[IS_NOT_ZERO_OR_NAN:%.*]] = fcmp one float [[ARG]], 0.000000e+00
; CHECK-NEXT: call void @llvm.assume(i1 noundef [[IS_NOT_ZERO_OR_NAN]]) #[[ATTR6]]
-; CHECK-NEXT: call void @extern.use(float [[ARG]])
+; CHECK-NEXT: call void @extern.use(float nofpclass(inf sub norm) [[ARG]])
; CHECK-NEXT: ret float [[ARG]]
;
entry:
@@ -447,12 +447,12 @@ entry:
}
define float @fcmp_olt_assume_une_0_callsite_arg_return(float %arg) {
-; CHECK-LABEL: define float @fcmp_olt_assume_une_0_callsite_arg_return
-; CHECK-SAME: (float returned [[ARG:%.*]]) {
+; CHECK-LABEL: define nofpclass(nan inf sub norm) float @fcmp_olt_assume_une_0_callsite_arg_return
+; CHECK-SAME: (float returned nofpclass(nan inf sub norm) [[ARG:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[IS_NOT_ZERO_OR_NAN:%.*]] = fcmp une float [[ARG]], 0.000000e+00
; CHECK-NEXT: call void @llvm.assume(i1 noundef [[IS_NOT_ZERO_OR_NAN]]) #[[ATTR6]]
-; CHECK-NEXT: call void @extern.use(float [[ARG]])
+; CHECK-NEXT: call void @extern.use(float nofpclass(nan inf sub norm) [[ARG]])
; CHECK-NEXT: ret float [[ARG]]
;
entry:
@@ -463,13 +463,13 @@ entry:
}
define half @fcmp_assume_issubnormal_callsite_arg_return(half %arg) {
-; CHECK-LABEL: define half @fcmp_assume_issubnormal_callsite_arg_return
-; CHECK-SAME: (half returned [[ARG:%.*]]) {
+; CHECK-LABEL: define nofpclass(zero sub) half @fcmp_assume_issubnormal_callsite_arg_return
+; CHECK-SAME: (half returned nofpclass(zero sub) [[ARG:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[FABS:%.*]] = call half @llvm.fabs.f16(half [[ARG]]) #[[ATTR6]]
+; CHECK-NEXT: [[FABS:%.*]] = call half @llvm.fabs.f16(half nofpclass(zero sub) [[ARG]]) #[[ATTR6]]
; CHECK-NEXT: [[IS_SUBNORMAL:%.*]] = fcmp olt half [[FABS]], 0xH0400
; CHECK-NEXT: call void @llvm.assume(i1 noundef [[IS_SUBNORMAL]]) #[[ATTR6]]
-; CHECK-NEXT: call void @extern.use.f16(half [[ARG]])
+; CHECK-NEXT: call void @extern.use.f16(half nofpclass(zero sub) [[ARG]])
; CHECK-NEXT: ret half [[ARG]]
;
entry:
@@ -499,15 +499,15 @@ entry:
; Assume not subnormal or zero, and not infinity
define half @fcmp_assume2_callsite_arg_return(half %arg) {
-; CHECK-LABEL: define half @fcmp_assume2_callsite_arg_return
-; CHECK-SAME: (half returned [[ARG:%.*]]) {
+; CHECK-LABEL: define nofpclass(inf norm) half @fcmp_assume2_callsite_arg_return
+; CHECK-SAME: (half returned nofpclass(inf norm) [[ARG:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[FABS:%.*]] = call half @llvm.fabs.f16(half [[ARG]]) #[[ATTR6]]
+; CHECK-NEXT: [[FABS:%.*]] = call nofpclass(inf) half @llvm.fabs.f16(half nofpclass(inf norm) [[ARG]]) #[[ATTR6]]
; CHECK-NEXT: [[NOT_SUBNORMAL_OR_ZERO:%.*]] = fcmp oge half [[FABS]], 0xH0400
; CHECK-NEXT: call void @llvm.assume(i1 noundef [[NOT_SUBNORMAL_OR_ZERO]]) #[[ATTR6]]
; CHECK-NEXT: [[NOT_INF:%.*]] = fcmp oeq half [[ARG]], 0xH7C00
; CHECK-NEXT: call void @llvm.assume(i1 noundef [[NOT_INF]]) #[[ATTR6]]
-; CHECK-NEXT: call void @extern.use.f16(half [[ARG]])
+; CHECK-NEXT: call void @extern.use.f16(half nofpclass(inf norm) [[ARG]])
; CHECK-NEXT: ret half [[ARG]]
;
entry:
@@ -523,12 +523,12 @@ entry:
}
define float @is_fpclass_assume_arg_return(float %arg) {
-; CHECK-LABEL: define float @is_fpclass_assume_arg_return
-; CHECK-SAME: (float returned [[ARG:%.*]]) {
+; CHECK-LABEL: define nofpclass(ninf nzero pnorm) float @is_fpclass_assume_arg_return
+; CHECK-SAME: (float returned nofpclass(ninf nzero pnorm) [[ARG:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[CLASS_TEST:%.*]] = call i1 @llvm.is.fpclass.f32(float [[ARG]], i32 noundef 292) #[[ATTR6]]
+; CHECK-NEXT: [[CLASS_TEST:%.*]] = call i1 @llvm.is.fpclass.f32(float nofpclass(ninf nzero pnorm) [[ARG]], i32 noundef 292) #[[ATTR6]]
; CHECK-NEXT: call void @llvm.assume(i1 noundef [[CLASS_TEST]]) #[[ATTR6]]
-; CHECK-NEXT: call void @extern.use(float [[ARG]])
+; CHECK-NEXT: call void @extern.use(float nofpclass(ninf nzero pnorm) [[ARG]])
; CHECK-NEXT: ret float [[ARG]]
;
entry:
@@ -541,16 +541,16 @@ entry:
; Make sure we don't get confused by looking at an unrelated assume
; based on the fabs of the value.
define half @assume_fcmp_fabs_with_other_fabs_assume(half %arg) {
-; CHECK-LABEL: define half @assume_fcmp_fabs_with_other_fabs_assume
-; CHECK-SAME: (half returned [[ARG:%.*]]) {
+; CHECK-LABEL: define nofpclass(zero sub) half @assume_fcmp_fabs_with_other_fabs_assume
+; CHECK-SAME: (half returned nofpclass(zero sub) [[ARG:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[FABS:%.*]] = call half @llvm.fabs.f16(half [[ARG]]) #[[ATTR6]]
+; CHECK-NEXT: [[FABS:%.*]] = call nofpclass(inf zero sub norm) half @llvm.fabs.f16(half nofpclass(zero sub) [[ARG]]) #[[ATTR6]]
; CHECK-NEXT: [[UNRELATED_FABS:%.*]] = fcmp one half [[FABS]], 0xH0000
; CHECK-NEXT: call void @llvm.assume(i1 noundef [[UNRELATED_FABS]]) #[[ATTR6]]
; CHECK-NEXT: [[IS_SUBNORMAL:%.*]] = fcmp olt half [[FABS]], 0xH0400
; CHECK-NEXT: call void @llvm.assume(i1 noundef [[IS_SUBNORMAL]]) #[[ATTR6]]
-; CHECK-NEXT: call void @extern.use.f16(half [[ARG]])
-; CHECK-NEXT: call void @extern.use.f16(half [[FABS]])
+; CHECK-NEXT: call void @extern.use.f16(half nofpclass(zero sub) [[ARG]])
+; CHECK-NEXT: call void @extern.use.f16(half nofpclass(inf zero sub norm) [[FABS]])
; CHECK-NEXT: ret half [[ARG]]
;
entry:
@@ -568,18 +568,18 @@ entry:
; Make sure if looking through the fabs finds a
diff erent source
; value, we still identify a test mask by ignoring the fabs
define half @assume_fcmp_fabs_with_other_fabs_assume_fallback(half %arg) {
-; CHECK-LABEL: define half @assume_fcmp_fabs_with_other_fabs_assume_fallback
-; CHECK-SAME: (half returned [[ARG:%.*]]) {
+; CHECK-LABEL: define nofpclass(pinf zero sub) half @assume_fcmp_fabs_with_other_fabs_assume_fallback
+; CHECK-SAME: (half returned nofpclass(pinf zero sub) [[ARG:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[FABS:%.*]] = call half @llvm.fabs.f16(half [[ARG]]) #[[ATTR6]]
+; CHECK-NEXT: [[FABS:%.*]] = call nofpclass(inf zero sub nnorm) half @llvm.fabs.f16(half nofpclass(pinf zero sub) [[ARG]]) #[[ATTR6]]
; CHECK-NEXT: [[ONE_INF:%.*]] = fcmp oeq half [[ARG]], 0xH7C00
; CHECK-NEXT: call void @llvm.assume(i1 noundef [[ONE_INF]]) #[[ATTR6]]
; CHECK-NEXT: [[UNRELATED_FABS:%.*]] = fcmp oeq half [[FABS]], 0xH0000
; CHECK-NEXT: call void @llvm.assume(i1 noundef [[UNRELATED_FABS]]) #[[ATTR6]]
; CHECK-NEXT: [[IS_SUBNORMAL:%.*]] = fcmp olt half [[FABS]], 0xH0400
; CHECK-NEXT: call void @llvm.assume(i1 noundef [[IS_SUBNORMAL]]) #[[ATTR6]]
-; CHECK-NEXT: call void @extern.use.f16(half [[ARG]])
-; CHECK-NEXT: call void @extern.use.f16(half [[FABS]])
+; CHECK-NEXT: call void @extern.use.f16(half nofpclass(pinf zero sub) [[ARG]])
+; CHECK-NEXT: call void @extern.use.f16(half nofpclass(inf zero sub nnorm) [[FABS]])
; CHECK-NEXT: ret half [[ARG]]
;
entry:
More information about the llvm-commits
mailing list