[Attributor] Avoid REQUIRED dependences in favor of OPTIONAL ones

When we use information only to short-cut deduction or improve it, we
can use OPTIONAL dependences instead of REQUIRED ones to avoid cascading
pessimistic fixpoints.

We also need to track dependences only when we use assumed information,
e.g., we act on assumed liveness information.
This commit is contained in:
Johannes Doerfert 2020-01-12 00:11:56 -06:00
parent 214ed3f676
commit 7ad17e008b
2 changed files with 37 additions and 17 deletions

View File

@ -3189,7 +3189,8 @@ struct AADereferenceableImpl : AADereferenceable {
for (const Attribute &Attr : Attrs)
takeKnownDerefBytesMaximum(Attr.getValueAsInt());
NonNullAA = &A.getAAFor<AANonNull>(*this, getIRPosition());
NonNullAA = &A.getAAFor<AANonNull>(*this, getIRPosition(),
/* TrackDependence */ false);
const IRPosition &IRP = this->getIRPosition();
bool IsFnInterface = IRP.isFnInterfaceKind();
@ -3628,9 +3629,10 @@ struct AAAlignCallSiteArgument final : AAAlignFloating {
ChangeStatus updateImpl(Attributor &A) override {
ChangeStatus Changed = AAAlignFloating::updateImpl(A);
if (Argument *Arg = getAssociatedArgument()) {
// We only take known information from the argument
// so we do not need to track a dependence.
const auto &ArgAlignAA = A.getAAFor<AAAlign>(
*this, IRPosition::argument(*Arg), /* TrackDependence */ false,
DepClassTy::OPTIONAL);
*this, IRPosition::argument(*Arg), /* TrackDependence */ false);
takeKnownMaximum(ArgAlignAA.getKnownAlign());
}
return Changed;
@ -3869,8 +3871,9 @@ struct AACaptureUseTracker final : public CaptureTracker {
bool isDereferenceableOrNull(Value *O, const DataLayout &DL) override {
if (CaptureTracker::isDereferenceableOrNull(O, DL))
return true;
const auto &DerefAA =
A.getAAFor<AADereferenceable>(NoCaptureAA, IRPosition::value(*O));
const auto &DerefAA = A.getAAFor<AADereferenceable>(
NoCaptureAA, IRPosition::value(*O), /* TrackDependence */ true,
DepClassTy::OPTIONAL);
return DerefAA.getAssumedDereferenceableBytes();
}
@ -3932,8 +3935,12 @@ struct AACaptureUseTracker final : public CaptureTracker {
/// See CaptureTracker::shouldExplore(...).
bool shouldExplore(const Use *U) override {
// Check liveness.
return !IsDeadAA.isAssumedDead(cast<Instruction>(U->getUser()));
// Check liveness, if it is used to stop exploring we need a dependence.
if (IsDeadAA.isAssumedDead(cast<Instruction>(U->getUser()))) {
A.recordDependence(IsDeadAA, NoCaptureAA, DepClassTy::OPTIONAL);
return false;
}
return true;
}
/// Update the state according to \p CapturedInMem, \p CapturedInInt, and
@ -3983,12 +3990,14 @@ ChangeStatus AANoCaptureImpl::updateImpl(Attributor &A) {
getArgNo() >= 0 ? IRP.getAssociatedFunction() : IRP.getAnchorScope();
assert(F && "Expected a function!");
const IRPosition &FnPos = IRPosition::function(*F);
const auto &IsDeadAA = A.getAAFor<AAIsDead>(*this, FnPos);
const auto &IsDeadAA =
A.getAAFor<AAIsDead>(*this, FnPos, /* TrackDependence */ false);
AANoCapture::StateType T;
// Readonly means we cannot capture through memory.
const auto &FnMemAA = A.getAAFor<AAMemoryBehavior>(*this, FnPos);
const auto &FnMemAA = A.getAAFor<AAMemoryBehavior>(
*this, FnPos, /* TrackDependence */ true, DepClassTy::OPTIONAL);
if (FnMemAA.isAssumedReadOnly()) {
T.addKnownBits(NOT_CAPTURED_IN_MEM);
if (FnMemAA.isKnownReadOnly())
@ -4013,11 +4022,15 @@ ChangeStatus AANoCaptureImpl::updateImpl(Attributor &A) {
return true;
};
const auto &NoUnwindAA = A.getAAFor<AANoUnwind>(*this, FnPos);
const auto &NoUnwindAA = A.getAAFor<AANoUnwind>(
*this, FnPos, /* TrackDependence */ true, DepClassTy::OPTIONAL);
if (NoUnwindAA.isAssumedNoUnwind()) {
bool IsVoidTy = F->getReturnType()->isVoidTy();
const AAReturnedValues *RVAA =
IsVoidTy ? nullptr : &A.getAAFor<AAReturnedValues>(*this, FnPos);
IsVoidTy ? nullptr
: &A.getAAFor<AAReturnedValues>(*this, FnPos,
/* TrackDependence */ true,
DepClassTy::OPTIONAL);
if (IsVoidTy || CheckReturnedArgs(*RVAA)) {
T.addKnownBits(NOT_CAPTURED_IN_RET);
if (T.isKnown(NOT_CAPTURED_IN_MEM))
@ -5013,7 +5026,8 @@ ChangeStatus AAMemoryBehaviorFloating::updateImpl(Attributor &A) {
AAMemoryBehavior::base_t FnMemAssumedState =
AAMemoryBehavior::StateType::getWorstState();
if (!Arg || !Arg->hasByValAttr()) {
const auto &FnMemAA = A.getAAFor<AAMemoryBehavior>(*this, FnPos);
const auto &FnMemAA = A.getAAFor<AAMemoryBehavior>(
*this, FnPos, /* TrackDependence */ true, DepClassTy::OPTIONAL);
FnMemAssumedState = FnMemAA.getAssumed();
S.addKnownBits(FnMemAA.getKnown());
if ((S.getAssumed() & FnMemAA.getAssumed()) == S.getAssumed())
@ -5037,7 +5051,8 @@ ChangeStatus AAMemoryBehaviorFloating::updateImpl(Attributor &A) {
// Liveness information to exclude dead users.
// TODO: Take the FnPos once we have call site specific liveness information.
const auto &LivenessAA = A.getAAFor<AAIsDead>(
*this, IRPosition::function(*IRP.getAssociatedFunction()));
*this, IRPosition::function(*IRP.getAssociatedFunction()),
/* TrackDependence */ false);
// Visit and expand uses until all are analyzed or a fixpoint is reached.
for (unsigned i = 0; i < Uses.size() && !isAtFixpoint(); i++) {
@ -5046,8 +5061,10 @@ ChangeStatus AAMemoryBehaviorFloating::updateImpl(Attributor &A) {
LLVM_DEBUG(dbgs() << "[AAMemoryBehavior] Use: " << **U << " in " << *UserI
<< " [Dead: " << (LivenessAA.isAssumedDead(UserI))
<< "]\n");
if (LivenessAA.isAssumedDead(UserI))
if (LivenessAA.isAssumedDead(UserI)) {
A.recordDependence(LivenessAA, *this, DepClassTy::OPTIONAL);
continue;
}
// Check if the users of UserI should also be visited.
if (followUsersOfUseIn(A, U, UserI))
@ -5085,7 +5102,8 @@ bool AAMemoryBehaviorFloating::followUsersOfUseIn(Attributor &A, const Use *U,
if (U->get()->getType()->isPointerTy()) {
unsigned ArgNo = ICS.getArgumentNo(U);
const auto &ArgNoCaptureAA = A.getAAFor<AANoCapture>(
*this, IRPosition::callsite_argument(ICS, ArgNo));
*this, IRPosition::callsite_argument(ICS, ArgNo),
/* TrackDependence */ true, DepClassTy::OPTIONAL);
return !ArgNoCaptureAA.isAssumedNoCapture();
}
@ -5140,7 +5158,9 @@ void AAMemoryBehaviorFloating::analyzeUseIn(Attributor &A, const Use *U,
Pos = IRPosition::callsite_argument(ICS, ICS.getArgumentNo(U));
else
Pos = IRPosition::callsite_function(ICS);
const auto &MemBehaviorAA = A.getAAFor<AAMemoryBehavior>(*this, Pos);
const auto &MemBehaviorAA = A.getAAFor<AAMemoryBehavior>(
*this, Pos,
/* TrackDependence */ true, DepClassTy::OPTIONAL);
// "assumed" has at most the same bits as the MemBehaviorAA assumed
// and at least "known".
intersectAssumedBits(MemBehaviorAA.getAssumed());

View File

@ -1,4 +1,4 @@
; RUN: opt -functionattrs -attributor -attributor-manifest-internal -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=6 -S < %s | FileCheck %s
; RUN: opt -functionattrs -attributor -attributor-manifest-internal -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=5 -S < %s | FileCheck %s
;
; Test cases specifically designed for the "no-capture" argument attribute.
; We use FIXME's to indicate problems and missing attributes.