[Attributor] Use structured deduction for AADereferenceable

Summary:
This is analogous to D66128 but for AADereferenceable. We have the logic
concentrated in the floating value updateImpl and we use the combiner
helper classes for arguments and return values.

The regressions will go away with "on-demand" attribute creation.
Improvements are already visible in the existing tests.

Reviewers: uenoku, sstefan1

Subscribers: hiraditya, bollu, jfb, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D66272

llvm-svn: 369329
This commit is contained in:
Johannes Doerfert 2019-08-20 06:08:35 +00:00
parent b9b8791fed
commit cfcca1a5b1
4 changed files with 92 additions and 157 deletions

View File

@ -1944,6 +1944,16 @@ struct DerefState : AbstractState {
}
};
template <>
ChangeStatus clampStateAndIndicateChange<DerefState>(DerefState &S,
const DerefState &R) {
ChangeStatus CS0 = clampStateAndIndicateChange<IntegerState>(
S.DerefBytesState, R.DerefBytesState);
ChangeStatus CS1 =
clampStateAndIndicateChange<IntegerState>(S.GlobalState, R.GlobalState);
return CS0 | CS1;
}
struct AADereferenceableImpl : AADereferenceable, DerefState {
AADereferenceableImpl(const IRPosition &IRP) : AADereferenceable(IRP) {}
using StateType = DerefState;
@ -1994,8 +2004,6 @@ struct AADereferenceableImpl : AADereferenceable, DerefState {
Attrs.emplace_back(Attribute::getWithDereferenceableOrNullBytes(
Ctx, getAssumedDereferenceableBytes()));
}
uint64_t computeAssumedDerefenceableBytes(Attributor &A, Value &V,
bool &IsGlobal);
/// See AbstractAttribute::getAsStr().
const std::string getAsStr() const override {
@ -2012,12 +2020,70 @@ private:
const AANonNull *NonNullAA = nullptr;
};
struct AADereferenceableReturned final : AADereferenceableImpl {
AADereferenceableReturned(const IRPosition &IRP)
/// Dereferenceable attribute for a floating value.
struct AADereferenceableFloating : AADereferenceableImpl {
AADereferenceableFloating(const IRPosition &IRP)
: AADereferenceableImpl(IRP) {}
/// See AbstractAttribute::updateImpl(...).
ChangeStatus updateImpl(Attributor &A) override;
ChangeStatus updateImpl(Attributor &A) override {
const DataLayout &DL = A.getDataLayout();
auto VisitValueCB = [&](Value &V, DerefState &T, bool Stripped) -> bool {
unsigned IdxWidth =
DL.getIndexSizeInBits(V.getType()->getPointerAddressSpace());
APInt Offset(IdxWidth, 0);
const Value *Base =
V.stripAndAccumulateInBoundsConstantOffsets(DL, Offset);
const auto *AA =
A.getAAFor<AADereferenceable>(*this, IRPosition::value(*Base));
int64_t DerefBytes = 0;
if (!AA || (!Stripped &&
getIRPosition().getPositionKind() == IRPosition::IRP_FLOAT)) {
// Use IR information if we did not strip anything.
// TODO: track globally.
bool CanBeNull;
DerefBytes = Base->getPointerDereferenceableBytes(DL, CanBeNull);
T.GlobalState.indicatePessimisticFixpoint();
} else {
const DerefState &DS = static_cast<const DerefState &>(AA->getState());
DerefBytes = DS.DerefBytesState.getAssumed();
T.GlobalState &= DS.GlobalState;
}
T.takeAssumedDerefBytesMinimum(
std::max(int64_t(0), DerefBytes - Offset.getSExtValue()));
if (!Stripped &&
getIRPosition().getPositionKind() == IRPosition::IRP_FLOAT) {
T.takeKnownDerefBytesMaximum(
std::max(int64_t(0), DerefBytes - Offset.getSExtValue()));
T.indicatePessimisticFixpoint();
}
return T.isValidState();
};
DerefState T;
if (!genericValueTraversal<AADereferenceable, DerefState>(
A, getIRPosition(), *this, T, VisitValueCB))
return indicatePessimisticFixpoint();
return clampStateAndIndicateChange(getState(), T);
}
/// See AbstractAttribute::trackStatistics()
void trackStatistics() const override {
STATS_DECLTRACK_FLOATING_ATTR(dereferenceable)
}
};
/// Dereferenceable attribute for a return value.
struct AADereferenceableReturned final
: AAReturnedFromReturnedValues<AADereferenceableImpl> {
AADereferenceableReturned(const IRPosition &IRP)
: AAReturnedFromReturnedValues<AADereferenceableImpl>(IRP) {}
/// See AbstractAttribute::trackStatistics()
void trackStatistics() const override {
@ -2025,133 +2091,21 @@ struct AADereferenceableReturned final : AADereferenceableImpl {
}
};
// Helper function that returns dereferenceable bytes.
static uint64_t calcDifferenceIfBaseIsNonNull(int64_t DerefBytes,
int64_t Offset, bool IsNonNull) {
if (!IsNonNull)
return 0;
return std::max((int64_t)0, DerefBytes - Offset);
}
uint64_t
AADereferenceableImpl::computeAssumedDerefenceableBytes(Attributor &A, Value &V,
bool &IsGlobal) {
// TODO: Tracking the globally flag.
IsGlobal = false;
// First, we try to get information about V from Attributor.
if (auto *DerefAA =
A.getAAFor<AADereferenceable>(*this, IRPosition::value(V))) {
return DerefAA->getAssumedDereferenceableBytes();
}
// Otherwise, we try to compute assumed bytes from base pointer.
const DataLayout &DL = A.getDataLayout();
unsigned IdxWidth =
DL.getIndexSizeInBits(V.getType()->getPointerAddressSpace());
APInt Offset(IdxWidth, 0);
Value *Base = V.stripAndAccumulateInBoundsConstantOffsets(DL, Offset);
if (auto *BaseDerefAA =
A.getAAFor<AADereferenceable>(*this, IRPosition::value(*Base))) {
return calcDifferenceIfBaseIsNonNull(
BaseDerefAA->getAssumedDereferenceableBytes(), Offset.getSExtValue(),
Offset != 0 || BaseDerefAA->isAssumedNonNull());
}
// Then, use IR information.
if (isDereferenceablePointer(Base, Base->getType(), DL))
return calcDifferenceIfBaseIsNonNull(
DL.getTypeStoreSize(Base->getType()->getPointerElementType()),
Offset.getSExtValue(),
!NullPointerIsDefined(getAnchorScope(),
V.getType()->getPointerAddressSpace()));
return 0;
}
ChangeStatus AADereferenceableReturned::updateImpl(Attributor &A) {
auto BeforeState = static_cast<DerefState>(*this);
bool IsGlobal = isAssumedGlobal();
auto CheckReturnValue = [&](Value &RV) -> bool {
takeAssumedDerefBytesMinimum(
computeAssumedDerefenceableBytes(A, RV, IsGlobal));
return isValidState();
};
if (A.checkForAllReturnedValues(CheckReturnValue, *this)) {
GlobalState.intersectAssumedBits(IsGlobal);
return BeforeState == static_cast<DerefState>(*this)
? ChangeStatus::UNCHANGED
: ChangeStatus::CHANGED;
}
return indicatePessimisticFixpoint();
}
struct AADereferenceableArgument final : AADereferenceableImpl {
/// Dereferenceable attribute for an argument
struct AADereferenceableArgument final
: AAArgumentFromCallSiteArguments<AADereferenceableImpl> {
AADereferenceableArgument(const IRPosition &IRP)
: AADereferenceableImpl(IRP) {}
/// See AbstractAttribute::updateImpl(...).
ChangeStatus updateImpl(Attributor &A) override;
: AAArgumentFromCallSiteArguments<AADereferenceableImpl>(IRP) {}
/// See AbstractAttribute::trackStatistics()
void trackStatistics() const override {
STATS_DECLTRACK_ARG_ATTR(dereferenceable)
}
void trackStatistics() const override{
STATS_DECLTRACK_ARG_ATTR(dereferenceable)};
};
ChangeStatus AADereferenceableArgument::updateImpl(Attributor &A) {
Argument &Arg = cast<Argument>(getAnchorValue());
auto BeforeState = static_cast<DerefState>(*this);
unsigned ArgNo = Arg.getArgNo();
bool IsGlobal = isAssumedGlobal();
// Callback function
std::function<bool(CallSite)> CallSiteCheck = [&](CallSite CS) -> bool {
assert(CS && "Sanity check: Call site was not initialized properly!");
// Check that DereferenceableAA is AADereferenceableCallSiteArgument.
if (auto *DereferenceableAA = A.getAAFor<AADereferenceable>(
*this, IRPosition::callsite_argument(CS, ArgNo))) {
ImmutableCallSite ICS(
&DereferenceableAA->getIRPosition().getAnchorValue());
if (ICS && CS.getInstruction() == ICS.getInstruction()) {
takeAssumedDerefBytesMinimum(
DereferenceableAA->getAssumedDereferenceableBytes());
IsGlobal &= DereferenceableAA->isAssumedGlobal();
return isValidState();
}
}
takeAssumedDerefBytesMinimum(computeAssumedDerefenceableBytes(
A, *CS.getArgOperand(ArgNo), IsGlobal));
return isValidState();
};
if (!A.checkForAllCallSites(CallSiteCheck, *this, true))
return indicatePessimisticFixpoint();
GlobalState.intersectAssumedBits(IsGlobal);
return BeforeState == static_cast<DerefState>(*this) ? ChangeStatus::UNCHANGED
: ChangeStatus::CHANGED;
}
/// Dereferenceable attribute for a call site argument.
struct AADereferenceableCallSiteArgument final : AADereferenceableImpl {
struct AADereferenceableCallSiteArgument final : AADereferenceableFloating {
AADereferenceableCallSiteArgument(const IRPosition &IRP)
: AADereferenceableImpl(IRP) {}
/// See AbstractAttribute::updateImpl(Attributor &A).
ChangeStatus updateImpl(Attributor &A) override;
: AADereferenceableFloating(IRP) {}
/// See AbstractAttribute::trackStatistics()
void trackStatistics() const override {
@ -2159,25 +2113,6 @@ struct AADereferenceableCallSiteArgument final : AADereferenceableImpl {
}
};
ChangeStatus AADereferenceableCallSiteArgument::updateImpl(Attributor &A) {
// NOTE: Never look at the argument of the callee in this method.
// If we do this, "dereferenceable" is always deduced because of the
// assumption.
Value &V = getAssociatedValue();
auto BeforeState = static_cast<DerefState>(*this);
bool IsGlobal = isAssumedGlobal();
takeAssumedDerefBytesMinimum(
computeAssumedDerefenceableBytes(A, V, IsGlobal));
GlobalState.intersectAssumedBits(IsGlobal);
return BeforeState == static_cast<DerefState>(*this) ? ChangeStatus::UNCHANGED
: ChangeStatus::CHANGED;
}
/// Dereferenceable attribute deduction for a call site return value.
using AADereferenceableCallSiteReturned = AADereferenceableReturned;
@ -2247,7 +2182,7 @@ struct AAAlignFloating : AAAlignImpl {
StateType T;
if (!genericValueTraversal<AAAlign, StateType>(A, getIRPosition(), *this, T,
VisitValueCB))
indicatePessimisticFixpoint();
return indicatePessimisticFixpoint();
// TODO: If we know we visited all incoming values, thus no are assumed
// dead, we can take the known information from the state T.

View File

@ -92,12 +92,12 @@ define internal i8* @f1(i8* readnone %0) local_unnamed_addr #0 {
; FIXME: Until we have "on-demand" attribute generation we do not determine the
; alignment for the return value here.
; define internal nonnull align 8 i8* @f1(i8* nonnull readnone align 8 %0)
; ATTRIBUTOR: define internal i8* @f1(i8* nonnull readnone align 8 %0)
; ATTRIBUTOR: define internal i8* @f1(i8* nonnull readnone align 8 dereferenceable(1) %0)
%2 = icmp eq i8* %0, null
br i1 %2, label %3, label %5
; <label>:3: ; preds = %1
; ATTRIBUTOR: %4 = tail call i8* @f2(i8* nonnull align 8 @a1)
; ATTRIBUTOR: %4 = tail call i8* @f2(i8* nonnull align 8 dereferenceable(1) @a1)
%4 = tail call i8* @f2(i8* nonnull @a1)
br label %5
@ -111,18 +111,18 @@ define internal i8* @f2(i8* readnone %0) local_unnamed_addr #0 {
; FIXME: Until we have "on-demand" attribute generation we do not determine the
; alignment for the return value here.
; define internal nonnull align 8 i8* @f2(i8* nonnull readnone align 8 %0)
; ATTRIBUTOR: define internal i8* @f2(i8* nonnull readnone align 8 %0)
; ATTRIBUTOR: define internal i8* @f2(i8* nonnull readnone align 8 dereferenceable(1) %0)
%2 = icmp eq i8* %0, null
br i1 %2, label %5, label %3
; <label>:3: ; preds = %1
; ATTRIBUTOR: %4 = tail call i8* @f1(i8* nonnull align 8 %0)
; ATTRIBUTOR: %4 = tail call i8* @f1(i8* nonnull align 8 dereferenceable(1) %0)
%4 = tail call i8* @f1(i8* nonnull %0)
br label %7
; <label>:5: ; preds = %1
; ATTRIBUTOR: %6 = tail call i8* @f3(i8* nonnull align 16 @a2)
; ATTRIBUTOR: %6 = tail call i8* @f3(i8* nonnull align 16 dereferenceable(1) @a2)
%6 = tail call i8* @f3(i8* nonnull @a2)
br label %7
@ -136,12 +136,12 @@ define internal i8* @f3(i8* readnone %0) local_unnamed_addr #0 {
; FIXME: Until we have "on-demand" attribute generation we do not determine the
; alignment for the return value here.
; define internal nonnull align 8 i8* @f3(i8* nonnull readnone align 16 %0)
; ATTRIBUTOR: define internal i8* @f3(i8* nonnull readnone align 16 %0)
; ATTRIBUTOR: define internal i8* @f3(i8* nonnull readnone align 16 dereferenceable(1) %0)
%2 = icmp eq i8* %0, null
br i1 %2, label %3, label %5
; <label>:3: ; preds = %1
; ATTRIBUTOR: %4 = tail call i8* @f1(i8* nonnull align 16 @a2)
; ATTRIBUTOR: %4 = tail call i8* @f1(i8* nonnull align 16 dereferenceable(1) @a2)
%4 = tail call i8* @f1(i8* nonnull @a2)
br label %5

View File

@ -23,7 +23,7 @@ define i32* @test2(i32* dereferenceable_or_null(4) %0, double* dereferenceable(8
; GEP inbounds
define i32* @test3_1(i32* dereferenceable(8) %0) local_unnamed_addr {
; define nonnull dereferenceable(4) i32* @test3_1(i32* nonnull dereferenceable(8) %0)
; ATTRIBUTOR: define dereferenceable_or_null(4) i32* @test3_1(i32* nonnull dereferenceable(8) %0)
; ATTRIBUTOR: define i32* @test3_1(i32* nonnull dereferenceable(8) %0)
%ret = getelementptr inbounds i32, i32* %0, i64 1
ret i32* %ret
}
@ -31,14 +31,14 @@ define i32* @test3_1(i32* dereferenceable(8) %0) local_unnamed_addr {
define i32* @test3_2(i32* dereferenceable_or_null(32) %0) local_unnamed_addr {
; FIXME: Argument should be mark dereferenceable because of GEP `inbounds`.
; define nonnull dereferenceable(16) i32* @test3_2(i32* dereferenceable_or_null(32) %0)
; ATTRIBUTOR: define dereferenceable_or_null(16) i32* @test3_2(i32* dereferenceable_or_null(32) %0)
; ATTRIBUTOR: define i32* @test3_2(i32* dereferenceable_or_null(32) %0)
%ret = getelementptr inbounds i32, i32* %0, i64 4
ret i32* %ret
}
define i32* @test3_3(i32* dereferenceable(8) %0, i32* dereferenceable(16) %1, i1 %2) local_unnamed_addr {
; define nonnull dereferenceable(4) i32* @test3_3(i32* nonnull dereferenceable(8) %0, i32* nonnull dereferenceable(16) %1, i1 %2) local_unnamed_addr
; ATTRIBUTOR: define dereferenceable_or_null(4) i32* @test3_3(i32* nonnull dereferenceable(8) %0, i32* nonnull dereferenceable(16) %1, i1 %2) local_unnamed_addr
; ATTRIBUTOR: define i32* @test3_3(i32* nonnull dereferenceable(8) %0, i32* nonnull dereferenceable(16) %1, i1 %2) local_unnamed_addr
%ret1 = getelementptr inbounds i32, i32* %0, i64 1
%ret2 = getelementptr inbounds i32, i32* %1, i64 2
%ret = select i1 %2, i32* %ret1, i32* %ret2

View File

@ -82,7 +82,7 @@ declare i8* @baz(...) nounwind uwtable
; FIXME: Until we have "on-demand" attribute generation we do not determine the
; alignment for the return value here.
; define nonnull align 8 dereferenceable(8) i8** @getter()
; CHECK: define dereferenceable_or_null(8) i8** @getter()
; CHECK: define i8** @getter()
define i8** @getter() {
ret i8** @G
}
@ -91,7 +91,7 @@ define i8** @getter() {
; alignment for the return value here.
; Returning global pointer. Should not be noalias.
; define nonnull align 8 dereferenceable(8) i8** @calle1()
; CHECK: define dereferenceable_or_null(8) i8** @calle1()
; CHECK: define i8** @calle1()
define i8** @calle1(){
%1 = call i8** @getter()
ret i8** %1