Skip to content

Commit

Permalink
[Attributor] Provide convenient helpers for isAssumedRead{None,Only}
Browse files Browse the repository at this point in the history
We have two attributes that can answer readnone queries. While there is
a dependence between them, it seems best to not force the users to know
what AA to ask. The helpers also allow to check for readonly nicely.

Test changes show where we now deduce readnone but haven't before,
mostly because we only asked AAMemoryBehavior and not AAMemoryLocation.
AANoAlias has not been ported to the new API yet.
  • Loading branch information
jdoerfert committed Feb 1, 2022
1 parent e140d51 commit adf0d57
Show file tree
Hide file tree
Showing 13 changed files with 188 additions and 184 deletions.
11 changes: 11 additions & 0 deletions llvm/include/llvm/Transforms/IPO/Attributor.h
Expand Up @@ -132,6 +132,7 @@ struct AbstractAttribute;
struct InformationCache;
struct AAIsDead;
struct AttributorCallGraph;
struct IRPosition;

class AAResults;
class Function;
Expand Down Expand Up @@ -205,6 +206,16 @@ bool getPotentialCopiesOfStoredValue(
Attributor &A, StoreInst &SI, SmallSetVector<Value *, 4> &PotentialCopies,
const AbstractAttribute &QueryingAA, bool &UsedAssumedInformation);

/// Return true if \p IRP is readonly. This will query respective AAs that
/// deduce the information and introduce dependences for \p QueryingAA.
bool isAssumedReadOnly(Attributor &A, const IRPosition &IRP,
const AbstractAttribute &QueryingAA, bool &IsKnown);

/// Return true if \p IRP is readnone. This will query respective AAs that
/// deduce the information and introduce dependences for \p QueryingAA.
bool isAssumedReadNone(Attributor &A, const IRPosition &IRP,
const AbstractAttribute &QueryingAA, bool &IsKnown);

} // namespace AA

/// The value passed to the line option that defines the maximal initialization
Expand Down
41 changes: 41 additions & 0 deletions llvm/lib/Transforms/IPO/Attributor.cpp
Expand Up @@ -395,6 +395,47 @@ bool AA::getPotentialCopiesOfStoredValue(
return true;
}

static bool isAssumedReadOnlyOrReadNone(Attributor &A, const IRPosition &IRP,
const AbstractAttribute &QueryingAA,
bool RequireReadNone, bool &IsKnown) {

IRPosition::Kind Kind = IRP.getPositionKind();
if (Kind == IRPosition::IRP_FUNCTION || Kind == IRPosition::IRP_CALL_SITE) {
const auto &MemLocAA =
A.getAAFor<AAMemoryLocation>(QueryingAA, IRP, DepClassTy::NONE);
if (MemLocAA.isAssumedReadNone()) {
IsKnown = MemLocAA.isKnownReadNone();
if (!IsKnown)
A.recordDependence(MemLocAA, QueryingAA, DepClassTy::OPTIONAL);
return true;
}
}

const auto &MemBehaviorAA =
A.getAAFor<AAMemoryBehavior>(QueryingAA, IRP, DepClassTy::NONE);
if (MemBehaviorAA.isAssumedReadNone() ||
(!RequireReadNone && MemBehaviorAA.isAssumedReadOnly())) {
IsKnown = RequireReadNone ? MemBehaviorAA.isKnownReadNone()
: MemBehaviorAA.isKnownReadOnly();
if (!IsKnown)
A.recordDependence(MemBehaviorAA, QueryingAA, DepClassTy::OPTIONAL);
return true;
}

return false;
}

bool AA::isAssumedReadOnly(Attributor &A, const IRPosition &IRP,
const AbstractAttribute &QueryingAA, bool &IsKnown) {
return isAssumedReadOnlyOrReadNone(A, IRP, QueryingAA,
/* RequireReadNone */ false, IsKnown);
}
bool AA::isAssumedReadNone(Attributor &A, const IRPosition &IRP,
const AbstractAttribute &QueryingAA, bool &IsKnown) {
return isAssumedReadOnlyOrReadNone(A, IRP, QueryingAA,
/* RequireReadNone */ true, IsKnown);
}

/// Return true if \p New is equal or worse than \p Old.
static bool isEqualOrWorse(const Attribute &New, const Attribute &Old) {
if (!Old.isIntAttribute())
Expand Down
48 changes: 15 additions & 33 deletions llvm/lib/Transforms/IPO/AttributorAttributes.cpp
Expand Up @@ -2893,16 +2893,10 @@ struct AAWillReturnImpl : public AAWillReturn {
(!getAssociatedFunction() || !getAssociatedFunction()->mustProgress()))
return false;

const auto &MemAA =
A.getAAFor<AAMemoryBehavior>(*this, getIRPosition(), DepClassTy::NONE);
if (!MemAA.isAssumedReadOnly())
return false;
if (KnownOnly && !MemAA.isKnownReadOnly())
return false;
if (!MemAA.isKnownReadOnly())
A.recordDependence(MemAA, *this, DepClassTy::OPTIONAL);

return true;
bool IsKnown;
if (AA::isAssumedReadOnly(A, getIRPosition(), *this, IsKnown))
return IsKnown || !KnownOnly;
return false;
}

/// See AbstractAttribute::updateImpl(...).
Expand Down Expand Up @@ -3107,9 +3101,8 @@ struct AANoAliasArgument final
return Base::updateImpl(A);

// If the argument is read-only, no-alias cannot break synchronization.
const auto &MemBehaviorAA = A.getAAFor<AAMemoryBehavior>(
*this, getIRPosition(), DepClassTy::OPTIONAL);
if (MemBehaviorAA.isAssumedReadOnly())
bool IsKnown;
if (AA::isAssumedReadOnly(A, getIRPosition(), *this, IsKnown))
return Base::updateImpl(A);

// If the argument is never passed through callbacks, no-alias cannot break
Expand Down Expand Up @@ -3465,14 +3458,8 @@ struct AAIsDeadValueImpl : public AAIsDead {
if (!NoUnwindAA.isKnownNoUnwind())
A.recordDependence(NoUnwindAA, *this, DepClassTy::OPTIONAL);

const auto &MemBehaviorAA =
A.getAndUpdateAAFor<AAMemoryBehavior>(*this, CallIRP, DepClassTy::NONE);
if (MemBehaviorAA.isAssumedReadOnly()) {
if (!MemBehaviorAA.isKnownReadOnly())
A.recordDependence(MemBehaviorAA, *this, DepClassTy::OPTIONAL);
return true;
}
return false;
bool IsKnown;
return AA::isAssumedReadOnly(A, CallIRP, *this, IsKnown);
}
};

Expand Down Expand Up @@ -5020,14 +5007,11 @@ ChangeStatus AANoCaptureImpl::updateImpl(Attributor &A) {
AANoCapture::StateType T;

// Readonly means we cannot capture through memory.
const auto &FnMemAA =
A.getAAFor<AAMemoryBehavior>(*this, FnPos, DepClassTy::NONE);
if (FnMemAA.isAssumedReadOnly()) {
bool IsKnown;
if (AA::isAssumedReadOnly(A, FnPos, *this, IsKnown)) {
T.addKnownBits(NOT_CAPTURED_IN_MEM);
if (FnMemAA.isKnownReadOnly())
if (IsKnown)
addKnownBits(NOT_CAPTURED_IN_MEM);
else
A.recordDependence(FnMemAA, *this, DepClassTy::OPTIONAL);
}

// Make sure all returned values are different than the underlying value.
Expand Down Expand Up @@ -5420,9 +5404,8 @@ struct AAValueSimplifyArgument final : AAValueSimplifyImpl {
if (Arg->hasByValAttr()) {
// TODO: We probably need to verify synchronization is not an issue, e.g.,
// there is no race by not copying a constant byval.
const auto &MemAA = A.getAAFor<AAMemoryBehavior>(*this, getIRPosition(),
DepClassTy::REQUIRED);
if (!MemAA.isAssumedReadOnly())
bool IsKnown;
if (!AA::isAssumedReadOnly(A, getIRPosition(), *this, IsKnown))
return indicatePessimisticFixpoint();
}

Expand Down Expand Up @@ -6922,9 +6905,8 @@ struct AAPrivatizablePtrCallSiteArgument final
return indicatePessimisticFixpoint();
}

const auto &MemBehaviorAA =
A.getAAFor<AAMemoryBehavior>(*this, IRP, DepClassTy::REQUIRED);
if (!MemBehaviorAA.isAssumedReadOnly()) {
bool IsKnown;
if (!AA::isAssumedReadOnly(A, IRP, *this, IsKnown)) {
LLVM_DEBUG(dbgs() << "[AAPrivatizablePtr] pointer is written!\n");
return indicatePessimisticFixpoint();
}
Expand Down
51 changes: 6 additions & 45 deletions llvm/test/Transforms/Attributor/ArgumentPromotion/fp80.ll
Expand Up @@ -22,23 +22,11 @@ target triple = "x86_64-unknown-linux-gnu"
;.
define void @run() {
;
; IS________OPM: Function Attrs: nofree noreturn nosync nounwind readnone
; IS________OPM-LABEL: define {{[^@]+}}@run
; IS________OPM-SAME: () #[[ATTR0:[0-9]+]] {
; IS________OPM-NEXT: entry:
; IS________OPM-NEXT: [[TMP0:%.*]] = call i64 @CaptureAStruct(%struct.Foo* nocapture nofree noundef nonnull readonly byval([[STRUCT_FOO:%.*]]) align 8 dereferenceable(16) @a) #[[ATTR0]]
; IS________OPM-NEXT: unreachable
;
; IS__TUNIT_NPM: Function Attrs: nofree noreturn nosync nounwind readnone
; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@run
; IS__TUNIT_NPM-SAME: () #[[ATTR0:[0-9]+]] {
; IS__TUNIT_NPM-NEXT: entry:
; IS__TUNIT_NPM-NEXT: [[A_CAST:%.*]] = bitcast %struct.Foo* @a to i32*
; IS__TUNIT_NPM-NEXT: [[TMP0:%.*]] = load i32, i32* [[A_CAST]], align 8
; IS__TUNIT_NPM-NEXT: [[A_0_1:%.*]] = getelementptr [[STRUCT_FOO:%.*]], %struct.Foo* @a, i64 0, i32 1
; IS__TUNIT_NPM-NEXT: [[TMP1:%.*]] = load i64, i64* [[A_0_1]], align 8
; IS__TUNIT_NPM-NEXT: [[TMP2:%.*]] = call i64 @CaptureAStruct(i32 [[TMP0]], i64 [[TMP1]]) #[[ATTR0]]
; IS__TUNIT_NPM-NEXT: unreachable
; NOT_CGSCC_NPM: Function Attrs: nofree noreturn nosync nounwind readnone willreturn
; NOT_CGSCC_NPM-LABEL: define {{[^@]+}}@run
; NOT_CGSCC_NPM-SAME: () #[[ATTR0:[0-9]+]] {
; NOT_CGSCC_NPM-NEXT: entry:
; NOT_CGSCC_NPM-NEXT: unreachable
;
; IS__CGSCC____: Function Attrs: nofree norecurse noreturn nosync nounwind readnone willreturn
; IS__CGSCC____-LABEL: define {{[^@]+}}@run
Expand Down Expand Up @@ -103,33 +91,6 @@ define internal i64 @CaptureAStruct(%struct.Foo* byval(%struct.Foo) %a) {
; IS__CGSCC_OPM-NEXT: [[GEP]] = getelementptr [[STRUCT_FOO:%.*]], %struct.Foo* [[A]], i64 0
; IS__CGSCC_OPM-NEXT: br label [[LOOP]]
;
; IS________OPM: Function Attrs: nofree noreturn nosync nounwind readnone
; IS________OPM-LABEL: define {{[^@]+}}@CaptureAStruct
; IS________OPM-SAME: (%struct.Foo* noalias nocapture nofree noundef nonnull readnone byval([[STRUCT_FOO:%.*]]) align 8 dereferenceable(16) [[A:%.*]]) #[[ATTR0]] {
; IS________OPM-NEXT: entry:
; IS________OPM-NEXT: [[A_PTR:%.*]] = alloca %struct.Foo*, align 8
; IS________OPM-NEXT: br label [[LOOP:%.*]]
; IS________OPM: loop:
; IS________OPM-NEXT: [[PHI:%.*]] = phi %struct.Foo* [ null, [[ENTRY:%.*]] ], [ [[A]], [[LOOP]] ]
; IS________OPM-NEXT: [[TMP0:%.*]] = phi %struct.Foo* [ [[A]], [[ENTRY]] ], [ [[TMP0]], [[LOOP]] ]
; IS________OPM-NEXT: br label [[LOOP]]
;
; IS__TUNIT_NPM: Function Attrs: nofree noreturn nosync nounwind readnone
; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@CaptureAStruct
; IS__TUNIT_NPM-SAME: (i32 [[TMP0:%.*]], i64 [[TMP1:%.*]]) #[[ATTR0]] {
; IS__TUNIT_NPM-NEXT: entry:
; IS__TUNIT_NPM-NEXT: [[A_PRIV:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8
; IS__TUNIT_NPM-NEXT: [[A_PRIV_CAST:%.*]] = bitcast %struct.Foo* [[A_PRIV]] to i32*
; IS__TUNIT_NPM-NEXT: store i32 [[TMP0]], i32* [[A_PRIV_CAST]], align 4
; IS__TUNIT_NPM-NEXT: [[A_PRIV_0_1:%.*]] = getelementptr [[STRUCT_FOO]], %struct.Foo* [[A_PRIV]], i64 0, i32 1
; IS__TUNIT_NPM-NEXT: store i64 [[TMP1]], i64* [[A_PRIV_0_1]], align 8
; IS__TUNIT_NPM-NEXT: [[A_PTR:%.*]] = alloca %struct.Foo*, align 8
; IS__TUNIT_NPM-NEXT: br label [[LOOP:%.*]]
; IS__TUNIT_NPM: loop:
; IS__TUNIT_NPM-NEXT: [[PHI:%.*]] = phi %struct.Foo* [ null, [[ENTRY:%.*]] ], [ [[A_PRIV]], [[LOOP]] ]
; IS__TUNIT_NPM-NEXT: [[TMP2:%.*]] = phi %struct.Foo* [ [[A_PRIV]], [[ENTRY]] ], [ [[TMP2]], [[LOOP]] ]
; IS__TUNIT_NPM-NEXT: br label [[LOOP]]
;
; IS__CGSCC____: Function Attrs: nofree norecurse noreturn nosync nounwind readnone
; IS__CGSCC____-LABEL: define {{[^@]+}}@CaptureAStruct
; IS__CGSCC____-SAME: (i32 [[TMP0:%.*]], i64 [[TMP1:%.*]]) #[[ATTR2:[0-9]+]] {
Expand All @@ -156,7 +117,7 @@ loop:
br label %loop
}
;.
; NOT_CGSCC_NPM: attributes #[[ATTR0:[0-9]+]] = { nofree noreturn nosync nounwind readnone }
; NOT_CGSCC_NPM: attributes #[[ATTR0]] = { nofree noreturn nosync nounwind readnone willreturn }
;.
; IS__CGSCC____: attributes #[[ATTR0]] = { nofree norecurse noreturn nosync nounwind readnone willreturn }
; IS__CGSCC____: attributes #[[ATTR1]] = { nofree norecurse nosync nounwind readnone willreturn }
Expand Down
@@ -1,6 +1,6 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
; RUN: opt -attributor -enable-new-pm=0 -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM
; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM
; RUN: opt -attributor -enable-new-pm=0 -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=5 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM
; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=5 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM
; RUN: opt -attributor-cgscc -enable-new-pm=0 -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_NPM,IS__CGSCC____,IS________OPM,IS__CGSCC_OPM
; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_OPM,IS__CGSCC____,IS________NPM,IS__CGSCC_NPM

Expand Down
@@ -1,6 +1,6 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
; RUN: opt -attributor -enable-new-pm=0 -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=8 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM
; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=8 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM
; RUN: opt -attributor -enable-new-pm=0 -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=10 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM
; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=10 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM
; RUN: opt -attributor-cgscc -enable-new-pm=0 -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_NPM,IS__CGSCC____,IS________OPM,IS__CGSCC_OPM
; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_OPM,IS__CGSCC____,IS________NPM,IS__CGSCC_NPM

Expand Down

0 comments on commit adf0d57

Please sign in to comment.