forked from OSchip/llvm-project
[Attributor][MustExec] Deduce dereferenceable and nonnull attribute using MustBeExecutedContextExplorer
Summary: In D65186 and related patches, MustBeExecutedContextExplorer is introduced. This enables us to traverse instructions guaranteed to execute from function entry. If we can know the argument is used as `dereferenceable` or `nonnull` in these instructions, we can mark `dereferenceable` or `nonnull` in the argument definition: 1. Memory instruction (similar to D64258) Trace memory instruction pointer operand. Currently, only inbounds GEPs are traced. ``` define i64* @f(i64* %a) { entry: %add.ptr = getelementptr inbounds i64, i64* %a, i64 1 ; (because of inbounds GEP we can know that %a is at least dereferenceable(16)) store i64 1, i64* %add.ptr, align 8 ret i64* %add.ptr ; dereferenceable 8 (because above instruction stores into it) } ``` 2. Propagation from callsite (similar to D27855) If `deref` or `nonnull` are known in call site parameter attributes we can also say that argument also that attribute. ``` declare void @use3(i8* %x, i8* %y, i8* %z); declare void @use3nonnull(i8* nonnull %x, i8* nonnull %y, i8* nonnull %z); define void @parent1(i8* %a, i8* %b, i8* %c) { call void @use3nonnull(i8* %b, i8* %c, i8* %a) ; Above instruction is always executed so we can say that@parent1(i8* nonnnull %a, i8* nonnull %b, i8* nonnull %c) call void @use3(i8* %c, i8* %a, i8* %b) ret void } ``` Reviewers: jdoerfert, sstefan1, spatel, reames Reviewed By: jdoerfert Subscribers: xbolva00, hiraditya, jfb, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D65402 llvm-svn: 374063
This commit is contained in:
parent
fb92ef1e55
commit
96e6ce4cd3
|
@ -101,6 +101,7 @@
|
||||||
#include "llvm/ADT/SetVector.h"
|
#include "llvm/ADT/SetVector.h"
|
||||||
#include "llvm/Analysis/AliasAnalysis.h"
|
#include "llvm/Analysis/AliasAnalysis.h"
|
||||||
#include "llvm/Analysis/CallGraph.h"
|
#include "llvm/Analysis/CallGraph.h"
|
||||||
|
#include "llvm/Analysis/MustExecute.h"
|
||||||
#include "llvm/Analysis/TargetLibraryInfo.h"
|
#include "llvm/Analysis/TargetLibraryInfo.h"
|
||||||
#include "llvm/IR/CallSite.h"
|
#include "llvm/IR/CallSite.h"
|
||||||
#include "llvm/IR/PassManager.h"
|
#include "llvm/IR/PassManager.h"
|
||||||
|
@ -595,7 +596,7 @@ private:
|
||||||
/// instance down in the abstract attributes.
|
/// instance down in the abstract attributes.
|
||||||
struct InformationCache {
|
struct InformationCache {
|
||||||
InformationCache(const Module &M, AnalysisGetter &AG)
|
InformationCache(const Module &M, AnalysisGetter &AG)
|
||||||
: DL(M.getDataLayout()), AG(AG) {
|
: DL(M.getDataLayout()), Explorer(/* ExploreInterBlock */ true), AG(AG) {
|
||||||
|
|
||||||
CallGraph *CG = AG.getAnalysis<CallGraphAnalysis>(M);
|
CallGraph *CG = AG.getAnalysis<CallGraphAnalysis>(M);
|
||||||
if (!CG)
|
if (!CG)
|
||||||
|
@ -626,6 +627,11 @@ struct InformationCache {
|
||||||
return FuncRWInstsMap[&F];
|
return FuncRWInstsMap[&F];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return MustBeExecutedContextExplorer
|
||||||
|
MustBeExecutedContextExplorer &getMustBeExecutedContextExplorer() {
|
||||||
|
return Explorer;
|
||||||
|
}
|
||||||
|
|
||||||
/// Return TargetLibraryInfo for function \p F.
|
/// Return TargetLibraryInfo for function \p F.
|
||||||
TargetLibraryInfo *getTargetLibraryInfoForFunction(const Function &F) {
|
TargetLibraryInfo *getTargetLibraryInfoForFunction(const Function &F) {
|
||||||
return AG.getAnalysis<TargetLibraryAnalysis>(F);
|
return AG.getAnalysis<TargetLibraryAnalysis>(F);
|
||||||
|
@ -663,6 +669,9 @@ private:
|
||||||
/// The datalayout used in the module.
|
/// The datalayout used in the module.
|
||||||
const DataLayout &DL;
|
const DataLayout &DL;
|
||||||
|
|
||||||
|
/// MustBeExecutedContextExplorer
|
||||||
|
MustBeExecutedContextExplorer Explorer;
|
||||||
|
|
||||||
/// Getters for analysis.
|
/// Getters for analysis.
|
||||||
AnalysisGetter &AG;
|
AnalysisGetter &AG;
|
||||||
|
|
||||||
|
@ -1714,6 +1723,11 @@ struct AADereferenceable
|
||||||
return NonNullAA && NonNullAA->isAssumedNonNull();
|
return NonNullAA && NonNullAA->isAssumedNonNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return true if we know that the underlying value is nonnull.
|
||||||
|
bool isKnownNonNull() const {
|
||||||
|
return NonNullAA && NonNullAA->isKnownNonNull();
|
||||||
|
}
|
||||||
|
|
||||||
/// Return true if we assume that underlying value is
|
/// Return true if we assume that underlying value is
|
||||||
/// dereferenceable(_or_null) globally.
|
/// dereferenceable(_or_null) globally.
|
||||||
bool isAssumedGlobal() const { return GlobalState.getAssumed(); }
|
bool isAssumedGlobal() const { return GlobalState.getAssumed(); }
|
||||||
|
|
|
@ -288,6 +288,35 @@ static bool addIfNotExistent(LLVMContext &Ctx, const Attribute &Attr,
|
||||||
|
|
||||||
llvm_unreachable("Expected enum or string attribute!");
|
llvm_unreachable("Expected enum or string attribute!");
|
||||||
}
|
}
|
||||||
|
static const Value *getPointerOperand(const Instruction *I) {
|
||||||
|
if (auto *LI = dyn_cast<LoadInst>(I))
|
||||||
|
if (!LI->isVolatile())
|
||||||
|
return LI->getPointerOperand();
|
||||||
|
|
||||||
|
if (auto *SI = dyn_cast<StoreInst>(I))
|
||||||
|
if (!SI->isVolatile())
|
||||||
|
return SI->getPointerOperand();
|
||||||
|
|
||||||
|
if (auto *CXI = dyn_cast<AtomicCmpXchgInst>(I))
|
||||||
|
if (!CXI->isVolatile())
|
||||||
|
return CXI->getPointerOperand();
|
||||||
|
|
||||||
|
if (auto *RMWI = dyn_cast<AtomicRMWInst>(I))
|
||||||
|
if (!RMWI->isVolatile())
|
||||||
|
return RMWI->getPointerOperand();
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
static const Value *getBasePointerOfAccessPointerOperand(const Instruction *I,
|
||||||
|
int64_t &BytesOffset,
|
||||||
|
const DataLayout &DL) {
|
||||||
|
const Value *Ptr = getPointerOperand(I);
|
||||||
|
if (!Ptr)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return GetPointerBaseWithConstantOffset(Ptr, BytesOffset, DL,
|
||||||
|
/*AllowNonInbounds*/ false);
|
||||||
|
}
|
||||||
|
|
||||||
ChangeStatus AbstractAttribute::update(Attributor &A) {
|
ChangeStatus AbstractAttribute::update(Attributor &A) {
|
||||||
ChangeStatus HasChanged = ChangeStatus::UNCHANGED;
|
ChangeStatus HasChanged = ChangeStatus::UNCHANGED;
|
||||||
|
@ -654,7 +683,8 @@ struct AAArgumentFromCallSiteArguments : public Base {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Helper class for generic replication: function returned -> cs returned.
|
/// Helper class for generic replication: function returned -> cs returned.
|
||||||
template <typename AAType, typename Base>
|
template <typename AAType, typename Base,
|
||||||
|
typename StateType = typename AAType::StateType>
|
||||||
struct AACallSiteReturnedFromReturned : public Base {
|
struct AACallSiteReturnedFromReturned : public Base {
|
||||||
AACallSiteReturnedFromReturned(const IRPosition &IRP) : Base(IRP) {}
|
AACallSiteReturnedFromReturned(const IRPosition &IRP) : Base(IRP) {}
|
||||||
|
|
||||||
|
@ -678,6 +708,80 @@ struct AACallSiteReturnedFromReturned : public Base {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Helper class for generic deduction using must-be-executed-context
|
||||||
|
/// Base class is required to have `followUse` method.
|
||||||
|
|
||||||
|
/// bool followUse(Attributor &A, const Use *U, const Instruction *I)
|
||||||
|
/// \param U Underlying use.
|
||||||
|
/// \param I The user of the \p U.
|
||||||
|
/// `followUse` returns true if the value should be tracked transitively.
|
||||||
|
|
||||||
|
template <typename AAType, typename Base,
|
||||||
|
typename StateType = typename AAType::StateType>
|
||||||
|
struct AAFromMustBeExecutedContext : public Base {
|
||||||
|
AAFromMustBeExecutedContext(const IRPosition &IRP) : Base(IRP) {}
|
||||||
|
|
||||||
|
void initialize(Attributor &A) override {
|
||||||
|
Base::initialize(A);
|
||||||
|
IRPosition &IRP = this->getIRPosition();
|
||||||
|
Instruction *CtxI = IRP.getCtxI();
|
||||||
|
|
||||||
|
if (!CtxI)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (const Use &U : IRP.getAssociatedValue().uses())
|
||||||
|
Uses.insert(&U);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See AbstractAttribute::updateImpl(...).
|
||||||
|
ChangeStatus updateImpl(Attributor &A) override {
|
||||||
|
auto BeforeState = this->getState();
|
||||||
|
auto &S = this->getState();
|
||||||
|
Instruction *CtxI = this->getIRPosition().getCtxI();
|
||||||
|
if (!CtxI)
|
||||||
|
return ChangeStatus::UNCHANGED;
|
||||||
|
|
||||||
|
MustBeExecutedContextExplorer &Explorer =
|
||||||
|
A.getInfoCache().getMustBeExecutedContextExplorer();
|
||||||
|
|
||||||
|
SetVector<const Use *> NextUses;
|
||||||
|
|
||||||
|
for (const Use *U : Uses) {
|
||||||
|
if (const Instruction *UserI = dyn_cast<Instruction>(U->getUser())) {
|
||||||
|
auto EIt = Explorer.begin(CtxI), EEnd = Explorer.end(CtxI);
|
||||||
|
bool Found = EIt.count(UserI);
|
||||||
|
while (!Found && ++EIt != EEnd)
|
||||||
|
Found = EIt.getCurrentInst() == UserI;
|
||||||
|
if (Found && Base::followUse(A, U, UserI))
|
||||||
|
for (const Use &Us : UserI->uses())
|
||||||
|
NextUses.insert(&Us);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const Use *U : NextUses)
|
||||||
|
Uses.insert(U);
|
||||||
|
|
||||||
|
return BeforeState == S ? ChangeStatus::UNCHANGED : ChangeStatus::CHANGED;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Container for (transitive) uses of the associated value.
|
||||||
|
SetVector<const Use *> Uses;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename AAType, typename Base,
|
||||||
|
typename StateType = typename AAType::StateType>
|
||||||
|
using AAArgumentFromCallSiteArgumentsAndMustBeExecutedContext =
|
||||||
|
AAComposeTwoGenericDeduction<AAType, Base, StateType,
|
||||||
|
AAFromMustBeExecutedContext,
|
||||||
|
AAArgumentFromCallSiteArguments>;
|
||||||
|
|
||||||
|
template <typename AAType, typename Base,
|
||||||
|
typename StateType = typename AAType::StateType>
|
||||||
|
using AACallSiteReturnedFromReturnedAndMustBeExecutedContext =
|
||||||
|
AAComposeTwoGenericDeduction<AAType, Base, StateType,
|
||||||
|
AAFromMustBeExecutedContext,
|
||||||
|
AACallSiteReturnedFromReturned>;
|
||||||
|
|
||||||
/// -----------------------NoUnwind Function Attribute--------------------------
|
/// -----------------------NoUnwind Function Attribute--------------------------
|
||||||
|
|
||||||
struct AANoUnwindImpl : AANoUnwind {
|
struct AANoUnwindImpl : AANoUnwind {
|
||||||
|
@ -1434,6 +1538,46 @@ struct AANoFreeCallSite final : AANoFreeImpl {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// ------------------------ NonNull Argument Attribute ------------------------
|
/// ------------------------ NonNull Argument Attribute ------------------------
|
||||||
|
static int64_t getKnownNonNullAndDerefBytesForUse(
|
||||||
|
Attributor &A, AbstractAttribute &QueryingAA, Value &AssociatedValue,
|
||||||
|
const Use *U, const Instruction *I, bool &IsNonNull, bool &TrackUse) {
|
||||||
|
// TODO: Add GEP support
|
||||||
|
TrackUse = false;
|
||||||
|
|
||||||
|
const Function *F = I->getFunction();
|
||||||
|
bool NullPointerIsDefined = F ? F->nullPointerIsDefined() : true;
|
||||||
|
const DataLayout &DL = A.getInfoCache().getDL();
|
||||||
|
if (ImmutableCallSite ICS = ImmutableCallSite(I)) {
|
||||||
|
if (ICS.isBundleOperand(U))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (ICS.isCallee(U)) {
|
||||||
|
IsNonNull |= !NullPointerIsDefined;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned ArgNo = ICS.getArgumentNo(U);
|
||||||
|
IRPosition IRP = IRPosition::callsite_argument(ICS, ArgNo);
|
||||||
|
auto &DerefAA = A.getAAFor<AADereferenceable>(QueryingAA, IRP);
|
||||||
|
IsNonNull |= DerefAA.isKnownNonNull();
|
||||||
|
return DerefAA.getKnownDereferenceableBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t Offset;
|
||||||
|
if (const Value *Base = getBasePointerOfAccessPointerOperand(I, Offset, DL)) {
|
||||||
|
if (Base == &AssociatedValue) {
|
||||||
|
int64_t DerefBytes =
|
||||||
|
Offset +
|
||||||
|
(int64_t)DL.getTypeStoreSize(
|
||||||
|
getPointerOperand(I)->getType()->getPointerElementType());
|
||||||
|
|
||||||
|
IsNonNull |= !NullPointerIsDefined;
|
||||||
|
return DerefBytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
struct AANonNullImpl : AANonNull {
|
struct AANonNullImpl : AANonNull {
|
||||||
AANonNullImpl(const IRPosition &IRP) : AANonNull(IRP) {}
|
AANonNullImpl(const IRPosition &IRP) : AANonNull(IRP) {}
|
||||||
|
|
||||||
|
@ -1445,6 +1589,16 @@ struct AANonNullImpl : AANonNull {
|
||||||
AANonNull::initialize(A);
|
AANonNull::initialize(A);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// See AAFromMustBeExecutedContext
|
||||||
|
bool followUse(Attributor &A, const Use *U, const Instruction *I) {
|
||||||
|
bool IsNonNull = false;
|
||||||
|
bool TrackUse = false;
|
||||||
|
getKnownNonNullAndDerefBytesForUse(A, *this, getAssociatedValue(), U, I,
|
||||||
|
IsNonNull, TrackUse);
|
||||||
|
takeKnownMaximum(IsNonNull);
|
||||||
|
return TrackUse;
|
||||||
|
}
|
||||||
|
|
||||||
/// See AbstractAttribute::getAsStr().
|
/// See AbstractAttribute::getAsStr().
|
||||||
const std::string getAsStr() const override {
|
const std::string getAsStr() const override {
|
||||||
return getAssumed() ? "nonnull" : "may-null";
|
return getAssumed() ? "nonnull" : "may-null";
|
||||||
|
@ -1452,12 +1606,14 @@ struct AANonNullImpl : AANonNull {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// NonNull attribute for a floating value.
|
/// NonNull attribute for a floating value.
|
||||||
struct AANonNullFloating : AANonNullImpl {
|
struct AANonNullFloating
|
||||||
AANonNullFloating(const IRPosition &IRP) : AANonNullImpl(IRP) {}
|
: AAFromMustBeExecutedContext<AANonNull, AANonNullImpl> {
|
||||||
|
using Base = AAFromMustBeExecutedContext<AANonNull, AANonNullImpl>;
|
||||||
|
AANonNullFloating(const IRPosition &IRP) : Base(IRP) {}
|
||||||
|
|
||||||
/// See AbstractAttribute::initialize(...).
|
/// See AbstractAttribute::initialize(...).
|
||||||
void initialize(Attributor &A) override {
|
void initialize(Attributor &A) override {
|
||||||
AANonNullImpl::initialize(A);
|
Base::initialize(A);
|
||||||
|
|
||||||
if (isAtFixpoint())
|
if (isAtFixpoint())
|
||||||
return;
|
return;
|
||||||
|
@ -1475,6 +1631,10 @@ struct AANonNullFloating : AANonNullImpl {
|
||||||
|
|
||||||
/// See AbstractAttribute::updateImpl(...).
|
/// See AbstractAttribute::updateImpl(...).
|
||||||
ChangeStatus updateImpl(Attributor &A) override {
|
ChangeStatus updateImpl(Attributor &A) override {
|
||||||
|
ChangeStatus Change = Base::updateImpl(A);
|
||||||
|
if (isKnownNonNull())
|
||||||
|
return Change;
|
||||||
|
|
||||||
const DataLayout &DL = A.getDataLayout();
|
const DataLayout &DL = A.getDataLayout();
|
||||||
|
|
||||||
auto VisitValueCB = [&](Value &V, AAAlign::StateType &T,
|
auto VisitValueCB = [&](Value &V, AAAlign::StateType &T,
|
||||||
|
@ -1518,9 +1678,12 @@ struct AANonNullReturned final
|
||||||
|
|
||||||
/// NonNull attribute for function argument.
|
/// NonNull attribute for function argument.
|
||||||
struct AANonNullArgument final
|
struct AANonNullArgument final
|
||||||
: AAArgumentFromCallSiteArguments<AANonNull, AANonNullImpl> {
|
: AAArgumentFromCallSiteArgumentsAndMustBeExecutedContext<AANonNull,
|
||||||
|
AANonNullImpl> {
|
||||||
AANonNullArgument(const IRPosition &IRP)
|
AANonNullArgument(const IRPosition &IRP)
|
||||||
: AAArgumentFromCallSiteArguments<AANonNull, AANonNullImpl>(IRP) {}
|
: AAArgumentFromCallSiteArgumentsAndMustBeExecutedContext<AANonNull,
|
||||||
|
AANonNullImpl>(
|
||||||
|
IRP) {}
|
||||||
|
|
||||||
/// See AbstractAttribute::trackStatistics()
|
/// See AbstractAttribute::trackStatistics()
|
||||||
void trackStatistics() const override { STATS_DECLTRACK_ARG_ATTR(nonnull) }
|
void trackStatistics() const override { STATS_DECLTRACK_ARG_ATTR(nonnull) }
|
||||||
|
@ -1535,9 +1698,12 @@ struct AANonNullCallSiteArgument final : AANonNullFloating {
|
||||||
|
|
||||||
/// NonNull attribute for a call site return position.
|
/// NonNull attribute for a call site return position.
|
||||||
struct AANonNullCallSiteReturned final
|
struct AANonNullCallSiteReturned final
|
||||||
: AACallSiteReturnedFromReturned<AANonNull, AANonNullImpl> {
|
: AACallSiteReturnedFromReturnedAndMustBeExecutedContext<AANonNull,
|
||||||
|
AANonNullImpl> {
|
||||||
AANonNullCallSiteReturned(const IRPosition &IRP)
|
AANonNullCallSiteReturned(const IRPosition &IRP)
|
||||||
: AACallSiteReturnedFromReturned<AANonNull, AANonNullImpl>(IRP) {}
|
: AACallSiteReturnedFromReturnedAndMustBeExecutedContext<AANonNull,
|
||||||
|
AANonNullImpl>(
|
||||||
|
IRP) {}
|
||||||
|
|
||||||
/// See AbstractAttribute::trackStatistics()
|
/// See AbstractAttribute::trackStatistics()
|
||||||
void trackStatistics() const override { STATS_DECLTRACK_CSRET_ATTR(nonnull) }
|
void trackStatistics() const override { STATS_DECLTRACK_CSRET_ATTR(nonnull) }
|
||||||
|
@ -2290,6 +2456,16 @@ struct AADereferenceableImpl : AADereferenceable {
|
||||||
const StateType &getState() const override { return *this; }
|
const StateType &getState() const override { return *this; }
|
||||||
/// }
|
/// }
|
||||||
|
|
||||||
|
/// See AAFromMustBeExecutedContext
|
||||||
|
bool followUse(Attributor &A, const Use *U, const Instruction *I) {
|
||||||
|
bool IsNonNull = false;
|
||||||
|
bool TrackUse = false;
|
||||||
|
int64_t DerefBytes = getKnownNonNullAndDerefBytesForUse(
|
||||||
|
A, *this, getAssociatedValue(), U, I, IsNonNull, TrackUse);
|
||||||
|
takeKnownDerefBytesMaximum(DerefBytes);
|
||||||
|
return TrackUse;
|
||||||
|
}
|
||||||
|
|
||||||
void getDeducedAttributes(LLVMContext &Ctx,
|
void getDeducedAttributes(LLVMContext &Ctx,
|
||||||
SmallVectorImpl<Attribute> &Attrs) const override {
|
SmallVectorImpl<Attribute> &Attrs) const override {
|
||||||
// TODO: Add *_globally support
|
// TODO: Add *_globally support
|
||||||
|
@ -2314,12 +2490,16 @@ struct AADereferenceableImpl : AADereferenceable {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Dereferenceable attribute for a floating value.
|
/// Dereferenceable attribute for a floating value.
|
||||||
struct AADereferenceableFloating : AADereferenceableImpl {
|
struct AADereferenceableFloating
|
||||||
AADereferenceableFloating(const IRPosition &IRP)
|
: AAFromMustBeExecutedContext<AADereferenceable, AADereferenceableImpl> {
|
||||||
: AADereferenceableImpl(IRP) {}
|
using Base =
|
||||||
|
AAFromMustBeExecutedContext<AADereferenceable, AADereferenceableImpl>;
|
||||||
|
AADereferenceableFloating(const IRPosition &IRP) : Base(IRP) {}
|
||||||
|
|
||||||
/// See AbstractAttribute::updateImpl(...).
|
/// See AbstractAttribute::updateImpl(...).
|
||||||
ChangeStatus updateImpl(Attributor &A) override {
|
ChangeStatus updateImpl(Attributor &A) override {
|
||||||
|
ChangeStatus Change = Base::updateImpl(A);
|
||||||
|
|
||||||
const DataLayout &DL = A.getDataLayout();
|
const DataLayout &DL = A.getDataLayout();
|
||||||
|
|
||||||
auto VisitValueCB = [&](Value &V, DerefState &T, bool Stripped) -> bool {
|
auto VisitValueCB = [&](Value &V, DerefState &T, bool Stripped) -> bool {
|
||||||
|
@ -2378,7 +2558,7 @@ struct AADereferenceableFloating : AADereferenceableImpl {
|
||||||
A, getIRPosition(), *this, T, VisitValueCB))
|
A, getIRPosition(), *this, T, VisitValueCB))
|
||||||
return indicatePessimisticFixpoint();
|
return indicatePessimisticFixpoint();
|
||||||
|
|
||||||
return clampStateAndIndicateChange(getState(), T);
|
return Change | clampStateAndIndicateChange(getState(), T);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See AbstractAttribute::trackStatistics()
|
/// See AbstractAttribute::trackStatistics()
|
||||||
|
@ -2403,12 +2583,11 @@ struct AADereferenceableReturned final
|
||||||
|
|
||||||
/// Dereferenceable attribute for an argument
|
/// Dereferenceable attribute for an argument
|
||||||
struct AADereferenceableArgument final
|
struct AADereferenceableArgument final
|
||||||
: AAArgumentFromCallSiteArguments<AADereferenceable, AADereferenceableImpl,
|
: AAArgumentFromCallSiteArgumentsAndMustBeExecutedContext<
|
||||||
DerefState> {
|
AADereferenceable, AADereferenceableImpl, DerefState> {
|
||||||
AADereferenceableArgument(const IRPosition &IRP)
|
using Base = AAArgumentFromCallSiteArgumentsAndMustBeExecutedContext<
|
||||||
: AAArgumentFromCallSiteArguments<AADereferenceable,
|
AADereferenceable, AADereferenceableImpl, DerefState>;
|
||||||
AADereferenceableImpl, DerefState>(
|
AADereferenceableArgument(const IRPosition &IRP) : Base(IRP) {}
|
||||||
IRP) {}
|
|
||||||
|
|
||||||
/// See AbstractAttribute::trackStatistics()
|
/// See AbstractAttribute::trackStatistics()
|
||||||
void trackStatistics() const override {
|
void trackStatistics() const override {
|
||||||
|
@ -2428,13 +2607,16 @@ struct AADereferenceableCallSiteArgument final : AADereferenceableFloating {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Dereferenceable attribute deduction for a call site return value.
|
/// Dereferenceable attribute deduction for a call site return value.
|
||||||
struct AADereferenceableCallSiteReturned final : AADereferenceableImpl {
|
struct AADereferenceableCallSiteReturned final
|
||||||
AADereferenceableCallSiteReturned(const IRPosition &IRP)
|
: AACallSiteReturnedFromReturnedAndMustBeExecutedContext<
|
||||||
: AADereferenceableImpl(IRP) {}
|
AADereferenceable, AADereferenceableImpl> {
|
||||||
|
using Base = AACallSiteReturnedFromReturnedAndMustBeExecutedContext<
|
||||||
|
AADereferenceable, AADereferenceableImpl>;
|
||||||
|
AADereferenceableCallSiteReturned(const IRPosition &IRP) : Base(IRP) {}
|
||||||
|
|
||||||
/// See AbstractAttribute::initialize(...).
|
/// See AbstractAttribute::initialize(...).
|
||||||
void initialize(Attributor &A) override {
|
void initialize(Attributor &A) override {
|
||||||
AADereferenceableImpl::initialize(A);
|
Base::initialize(A);
|
||||||
Function *F = getAssociatedFunction();
|
Function *F = getAssociatedFunction();
|
||||||
if (!F)
|
if (!F)
|
||||||
indicatePessimisticFixpoint();
|
indicatePessimisticFixpoint();
|
||||||
|
@ -2446,11 +2628,14 @@ struct AADereferenceableCallSiteReturned final : AADereferenceableImpl {
|
||||||
// call site specific liveness information and then it makes
|
// call site specific liveness information and then it makes
|
||||||
// sense to specialize attributes for call sites arguments instead of
|
// sense to specialize attributes for call sites arguments instead of
|
||||||
// redirecting requests to the callee argument.
|
// redirecting requests to the callee argument.
|
||||||
|
|
||||||
|
ChangeStatus Change = Base::updateImpl(A);
|
||||||
Function *F = getAssociatedFunction();
|
Function *F = getAssociatedFunction();
|
||||||
const IRPosition &FnPos = IRPosition::returned(*F);
|
const IRPosition &FnPos = IRPosition::returned(*F);
|
||||||
auto &FnAA = A.getAAFor<AADereferenceable>(*this, FnPos);
|
auto &FnAA = A.getAAFor<AADereferenceable>(*this, FnPos);
|
||||||
return clampStateAndIndicateChange(
|
return Change |
|
||||||
getState(), static_cast<const DerefState &>(FnAA.getState()));
|
clampStateAndIndicateChange(
|
||||||
|
getState(), static_cast<const DerefState &>(FnAA.getState()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See AbstractAttribute::trackStatistics()
|
/// See AbstractAttribute::trackStatistics()
|
||||||
|
|
|
@ -175,7 +175,7 @@ define void @test9_traversal(i1 %c, i32* align 4 %B, i32* align 8 %C) {
|
||||||
|
|
||||||
; FIXME: This will work with an upcoming patch (D66618 or similar)
|
; FIXME: This will work with an upcoming patch (D66618 or similar)
|
||||||
; define align 32 i32* @test10a(i32* align 32 "no-capture-maybe-returned" %p)
|
; define align 32 i32* @test10a(i32* align 32 "no-capture-maybe-returned" %p)
|
||||||
; ATTRIBUTOR: define i32* @test10a(i32* align 32 "no-capture-maybe-returned" %p)
|
; ATTRIBUTOR: define i32* @test10a(i32* nonnull align 32 dereferenceable(4) "no-capture-maybe-returned" %p)
|
||||||
define i32* @test10a(i32* align 32 %p) {
|
define i32* @test10a(i32* align 32 %p) {
|
||||||
; ATTRIBUTOR: %l = load i32, i32* %p, align 32
|
; ATTRIBUTOR: %l = load i32, i32* %p, align 32
|
||||||
%l = load i32, i32* %p
|
%l = load i32, i32* %p
|
||||||
|
@ -203,7 +203,7 @@ e:
|
||||||
|
|
||||||
; FIXME: This will work with an upcoming patch (D66618 or similar)
|
; FIXME: This will work with an upcoming patch (D66618 or similar)
|
||||||
; define align 32 i32* @test10b(i32* align 32 "no-capture-maybe-returned" %p)
|
; define align 32 i32* @test10b(i32* align 32 "no-capture-maybe-returned" %p)
|
||||||
; ATTRIBUTOR: define i32* @test10b(i32* align 32 "no-capture-maybe-returned" %p)
|
; ATTRIBUTOR: define i32* @test10b(i32* nonnull align 32 dereferenceable(4) "no-capture-maybe-returned" %p)
|
||||||
define i32* @test10b(i32* align 32 %p) {
|
define i32* @test10b(i32* align 32 %p) {
|
||||||
; ATTRIBUTOR: %l = load i32, i32* %p, align 32
|
; ATTRIBUTOR: %l = load i32, i32* %p, align 32
|
||||||
%l = load i32, i32* %p
|
%l = load i32, i32* %p
|
||||||
|
|
|
@ -244,7 +244,8 @@ declare i32 @printf(i8* nocapture, ...)
|
||||||
; }
|
; }
|
||||||
;
|
;
|
||||||
; There should *not* be a no-capture attribute on %a
|
; There should *not* be a no-capture attribute on %a
|
||||||
; CHECK: define i64* @not_captured_but_returned_0(i64* returned writeonly "no-capture-maybe-returned" %a)
|
; CHECK: define nonnull dereferenceable(8) i64* @not_captured_but_returned_0(i64* nonnull returned writeonly dereferenceable(8) "no-capture-maybe-returned" %a)
|
||||||
|
|
||||||
define i64* @not_captured_but_returned_0(i64* %a) #0 {
|
define i64* @not_captured_but_returned_0(i64* %a) #0 {
|
||||||
entry:
|
entry:
|
||||||
store i64 0, i64* %a, align 8
|
store i64 0, i64* %a, align 8
|
||||||
|
@ -354,6 +355,7 @@ entry:
|
||||||
;
|
;
|
||||||
; CHECK: define i32* @ret_arg_or_unknown(i32* readnone %b)
|
; CHECK: define i32* @ret_arg_or_unknown(i32* readnone %b)
|
||||||
; CHECK: define i32* @ret_arg_or_unknown_through_phi(i32* readnone %b)
|
; CHECK: define i32* @ret_arg_or_unknown_through_phi(i32* readnone %b)
|
||||||
|
|
||||||
declare i32* @unknown()
|
declare i32* @unknown()
|
||||||
|
|
||||||
define i32* @ret_arg_or_unknown(i32* %b) #0 {
|
define i32* @ret_arg_or_unknown(i32* %b) #0 {
|
||||||
|
|
|
@ -254,7 +254,7 @@ return: ; preds = %cond.end, %if.then3
|
||||||
;
|
;
|
||||||
; FNATTR: define i32* @rt0(i32* readonly %a)
|
; FNATTR: define i32* @rt0(i32* readonly %a)
|
||||||
; BOTH: Function Attrs: nofree noinline noreturn nosync nounwind readonly uwtable
|
; BOTH: Function Attrs: nofree noinline noreturn nosync nounwind readonly uwtable
|
||||||
; BOTH-NEXT: define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @rt0(i32* nocapture readonly %a)
|
; BOTH-NEXT: define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @rt0(i32* nocapture nonnull readonly dereferenceable(4) %a)
|
||||||
define i32* @rt0(i32* %a) #0 {
|
define i32* @rt0(i32* %a) #0 {
|
||||||
entry:
|
entry:
|
||||||
%v = load i32, i32* %a, align 4
|
%v = load i32, i32* %a, align 4
|
||||||
|
@ -272,7 +272,7 @@ entry:
|
||||||
;
|
;
|
||||||
; FNATTR: define noalias i32* @rt1(i32* nocapture readonly %a)
|
; FNATTR: define noalias i32* @rt1(i32* nocapture readonly %a)
|
||||||
; BOTH: Function Attrs: nofree noinline noreturn nosync nounwind readonly uwtable
|
; BOTH: Function Attrs: nofree noinline noreturn nosync nounwind readonly uwtable
|
||||||
; BOTH-NEXT: define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @rt1(i32* nocapture readonly %a)
|
; BOTH-NEXT: define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @rt1(i32* nocapture nonnull readonly dereferenceable(4) %a)
|
||||||
define i32* @rt1(i32* %a) #0 {
|
define i32* @rt1(i32* %a) #0 {
|
||||||
entry:
|
entry:
|
||||||
%v = load i32, i32* %a, align 4
|
%v = load i32, i32* %a, align 4
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
||||||
; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s
|
; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=2 < %s | FileCheck %s
|
||||||
; ModuleID = 'callback_simple.c'
|
; ModuleID = 'callback_simple.c'
|
||||||
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
|
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16
|
||||||
; FIXME: The callee -> call site direction is not working yet.
|
; FIXME: The callee -> call site direction is not working yet.
|
||||||
|
|
||||||
define void @t0_caller(i32* %a) {
|
define void @t0_caller(i32* %a) {
|
||||||
; CHECK: @t0_caller(i32* [[A:%.*]])
|
; CHECK-LABEL: @t0_caller(
|
||||||
; CHECK-NEXT: entry:
|
; CHECK-NEXT: entry:
|
||||||
; CHECK-NEXT: [[B:%.*]] = alloca i32, align 32
|
; CHECK-NEXT: [[B:%.*]] = alloca i32, align 32
|
||||||
; CHECK-NEXT: [[C:%.*]] = alloca i32*, align 64
|
; CHECK-NEXT: [[C:%.*]] = alloca i32*, align 64
|
||||||
|
@ -39,7 +39,7 @@ entry:
|
||||||
; Note that the first two arguments are provided by the callback_broker according to the callback in !1 below!
|
; Note that the first two arguments are provided by the callback_broker according to the callback in !1 below!
|
||||||
; The others are annotated with alignment information, amongst others, or even replaced by the constants passed to the call.
|
; The others are annotated with alignment information, amongst others, or even replaced by the constants passed to the call.
|
||||||
define internal void @t0_callback_callee(i32* %is_not_null, i32* %ptr, i32* %a, i64 %b, i32** %c) {
|
define internal void @t0_callback_callee(i32* %is_not_null, i32* %ptr, i32* %a, i64 %b, i32** %c) {
|
||||||
; CHECK: @t0_callback_callee(i32* nocapture writeonly [[IS_NOT_NULL:%.*]], i32* nocapture readonly [[PTR:%.*]], i32* [[A:%.*]], i64 [[B:%.*]], i32** nocapture nonnull readonly align 64 dereferenceable(8) [[C:%.*]])
|
; CHECK-LABEL: @t0_callback_callee(
|
||||||
; CHECK-NEXT: entry:
|
; CHECK-NEXT: entry:
|
||||||
; CHECK-NEXT: [[PTR_VAL:%.*]] = load i32, i32* [[PTR:%.*]], align 8
|
; CHECK-NEXT: [[PTR_VAL:%.*]] = load i32, i32* [[PTR:%.*]], align 8
|
||||||
; CHECK-NEXT: store i32 [[PTR_VAL]], i32* [[IS_NOT_NULL:%.*]]
|
; CHECK-NEXT: store i32 [[PTR_VAL]], i32* [[IS_NOT_NULL:%.*]]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
; RUN: opt -attributor -attributor-manifest-internal --attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR
|
; RUN: opt -attributor -attributor-manifest-internal --attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR
|
||||||
|
|
||||||
|
|
||||||
declare void @deref_phi_user(i32* %a);
|
declare void @deref_phi_user(i32* %a);
|
||||||
|
@ -111,3 +111,94 @@ for.inc: ; preds = %for.body
|
||||||
for.end: ; preds = %for.cond.cleanup
|
for.end: ; preds = %for.cond.cleanup
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; TEST 7
|
||||||
|
; share known infomation in must-be-executed-context
|
||||||
|
declare i32* @unkown_ptr() willreturn nounwind
|
||||||
|
declare i32 @unkown_f(i32*) willreturn nounwind
|
||||||
|
define i32* @f7_0(i32* %ptr) {
|
||||||
|
; ATTRIBUTOR: define nonnull dereferenceable(8) i32* @f7_0(i32* nonnull returned dereferenceable(8) %ptr)
|
||||||
|
%T = tail call i32 @unkown_f(i32* dereferenceable(8) %ptr)
|
||||||
|
ret i32* %ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
; ATTRIBUTOR: define void @f7_1(i32* nonnull dereferenceable(4) %ptr, i1 %c)
|
||||||
|
define void @f7_1(i32* %ptr, i1 %c) {
|
||||||
|
|
||||||
|
; ATTRIBUTOR: %A = tail call i32 @unkown_f(i32* nonnull dereferenceable(4) %ptr)
|
||||||
|
%A = tail call i32 @unkown_f(i32* %ptr)
|
||||||
|
|
||||||
|
%ptr.0 = load i32, i32* %ptr
|
||||||
|
; deref 4 hold
|
||||||
|
|
||||||
|
; FIXME: this should be %B = tail call i32 @unkown_f(i32* nonnull dereferenceable(4) %ptr)
|
||||||
|
; ATTRIBUTOR: %B = tail call i32 @unkown_f(i32* nonnull dereferenceable(4) %ptr)
|
||||||
|
%B = tail call i32 @unkown_f(i32* dereferenceable(1) %ptr)
|
||||||
|
|
||||||
|
br i1%c, label %if.true, label %if.false
|
||||||
|
if.true:
|
||||||
|
; ATTRIBUTOR: %C = tail call i32 @unkown_f(i32* nonnull dereferenceable(8) %ptr)
|
||||||
|
%C = tail call i32 @unkown_f(i32* %ptr)
|
||||||
|
|
||||||
|
; ATTRIBUTOR: %D = tail call i32 @unkown_f(i32* nonnull dereferenceable(8) %ptr)
|
||||||
|
%D = tail call i32 @unkown_f(i32* dereferenceable(8) %ptr)
|
||||||
|
|
||||||
|
; FIXME: This should be tail call i32 @unkown_f(i32* nonnull dereferenceable(8) %ptr)
|
||||||
|
; Making must-be-executed-context backward exploration will fix this.
|
||||||
|
; ATTRIBUTOR: %E = tail call i32 @unkown_f(i32* nonnull dereferenceable(4) %ptr)
|
||||||
|
%E = tail call i32 @unkown_f(i32* %ptr)
|
||||||
|
|
||||||
|
ret void
|
||||||
|
|
||||||
|
if.false:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; ATTRIBUTOR: define void @f7_2(i1 %c)
|
||||||
|
define void @f7_2(i1 %c) {
|
||||||
|
|
||||||
|
%ptr = tail call i32* @unkown_ptr()
|
||||||
|
|
||||||
|
; ATTRIBUTOR: %A = tail call i32 @unkown_f(i32* nonnull dereferenceable(4) %ptr)
|
||||||
|
%A = tail call i32 @unkown_f(i32* %ptr)
|
||||||
|
|
||||||
|
%arg_a.0 = load i32, i32* %ptr
|
||||||
|
; deref 4 hold
|
||||||
|
|
||||||
|
; ATTRIBUTOR: %B = tail call i32 @unkown_f(i32* nonnull dereferenceable(4) %ptr)
|
||||||
|
%B = tail call i32 @unkown_f(i32* dereferenceable(1) %ptr)
|
||||||
|
|
||||||
|
br i1%c, label %if.true, label %if.false
|
||||||
|
if.true:
|
||||||
|
|
||||||
|
; ATTRIBUTOR: %C = tail call i32 @unkown_f(i32* nonnull dereferenceable(8) %ptr)
|
||||||
|
%C = tail call i32 @unkown_f(i32* %ptr)
|
||||||
|
|
||||||
|
; ATTRIBUTOR: %D = tail call i32 @unkown_f(i32* nonnull dereferenceable(8) %ptr)
|
||||||
|
%D = tail call i32 @unkown_f(i32* dereferenceable(8) %ptr)
|
||||||
|
|
||||||
|
%E = tail call i32 @unkown_f(i32* %ptr)
|
||||||
|
; FIXME: This should be @unkown_f(i32* nonnull dereferenceable(8) %ptr)
|
||||||
|
; Making must-be-executed-context backward exploration will fix this.
|
||||||
|
; ATTRIBUTOR: %E = tail call i32 @unkown_f(i32* nonnull dereferenceable(4) %ptr)
|
||||||
|
|
||||||
|
ret void
|
||||||
|
|
||||||
|
if.false:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define i32* @f7_3() {
|
||||||
|
; ATTRIBUTOR: define nonnull dereferenceable(4) i32* @f7_3()
|
||||||
|
%ptr = tail call i32* @unkown_ptr()
|
||||||
|
store i32 10, i32* %ptr, align 16
|
||||||
|
ret i32* %ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
define i32* @test_for_minus_index(i32* %p) {
|
||||||
|
; FIXME: This should be define nonnull dereferenceable(8) i32* @test_for_minus_index(i32* nonnull %p)
|
||||||
|
; ATTRIBUTOR: define nonnull dereferenceable(8) i32* @test_for_minus_index(i32* writeonly "no-capture-maybe-returned" %p)
|
||||||
|
%q = getelementptr inbounds i32, i32* %p, i32 -2
|
||||||
|
store i32 1, i32* %q
|
||||||
|
ret i32* %q
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ entry:
|
||||||
ret i32 %add
|
ret i32 %add
|
||||||
}
|
}
|
||||||
|
|
||||||
; CHECK: define private i32 @noalias_args(i32* nocapture readonly %A, i32* noalias nocapture readonly %B)
|
; CHECK: define private i32 @noalias_args(i32* nocapture nonnull readonly dereferenceable(4) %A, i32* noalias nocapture nonnull readonly dereferenceable(4) %B)
|
||||||
|
|
||||||
define private i32 @noalias_args(i32* %A, i32* %B) #0 {
|
define private i32 @noalias_args(i32* %A, i32* %B) #0 {
|
||||||
entry:
|
entry:
|
||||||
|
@ -23,7 +23,8 @@ entry:
|
||||||
|
|
||||||
; FIXME: Should be something like this.
|
; FIXME: Should be something like this.
|
||||||
; define internal i32 @noalias_args_argmem(i32* noalias nocapture readonly %A, i32* noalias nocapture readonly %B)
|
; define internal i32 @noalias_args_argmem(i32* noalias nocapture readonly %A, i32* noalias nocapture readonly %B)
|
||||||
; CHECK: define internal i32 @noalias_args_argmem(i32* nocapture readonly %A, i32* nocapture readonly %B)
|
; CHECK: define internal i32 @noalias_args_argmem(i32* nocapture nonnull readonly dereferenceable(4) %A, i32* nocapture nonnull readonly dereferenceable(4) %B)
|
||||||
|
|
||||||
;
|
;
|
||||||
define internal i32 @noalias_args_argmem(i32* %A, i32* %B) #1 {
|
define internal i32 @noalias_args_argmem(i32* %A, i32* %B) #1 {
|
||||||
entry:
|
entry:
|
||||||
|
|
|
@ -40,7 +40,7 @@ define i32 @volatile_load(i32*) norecurse nounwind uwtable {
|
||||||
}
|
}
|
||||||
|
|
||||||
; CHECK: Function Attrs: nofree norecurse nosync nounwind readonly uwtable willreturn
|
; CHECK: Function Attrs: nofree norecurse nosync nounwind readonly uwtable willreturn
|
||||||
; CHECK-NEXT: define internal i32 @internal_load(i32* nocapture nonnull readonly %0)
|
; CHECK-NEXT: define internal i32 @internal_load(i32* nocapture nonnull readonly dereferenceable(4) %0)
|
||||||
define internal i32 @internal_load(i32*) norecurse nounwind uwtable {
|
define internal i32 @internal_load(i32*) norecurse nounwind uwtable {
|
||||||
%2 = load i32, i32* %0, align 4
|
%2 = load i32, i32* %0, align 4
|
||||||
ret i32 %2
|
ret i32 %2
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s
|
; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=2 < %s | FileCheck %s
|
||||||
|
|
||||||
; TEST 1 - negative.
|
; TEST 1 - negative.
|
||||||
|
|
||||||
|
|
|
@ -120,7 +120,9 @@ define void @nc2(i32* %p, i32* %q) {
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; EITHER: define void @nc3(void ()* nocapture %p)
|
|
||||||
|
; FNATTR: define void @nc3(void ()* nocapture %p)
|
||||||
|
; ATTRIBUTOR: define void @nc3(void ()* nocapture nonnull %p)
|
||||||
define void @nc3(void ()* %p) {
|
define void @nc3(void ()* %p) {
|
||||||
call void %p()
|
call void %p()
|
||||||
ret void
|
ret void
|
||||||
|
@ -133,7 +135,8 @@ define void @nc4(i8* %p) {
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; EITHER: define void @nc5(void (i8*)* nocapture %f, i8* nocapture %p)
|
; FNATTR: define void @nc5(void (i8*)* nocapture %f, i8* nocapture %p)
|
||||||
|
; ATTRIBUTOR: define void @nc5(void (i8*)* nocapture nonnull %f, i8* nocapture %p)
|
||||||
define void @nc5(void (i8*)* %f, i8* %p) {
|
define void @nc5(void (i8*)* %f, i8* %p) {
|
||||||
call void %f(i8* %p) readonly nounwind
|
call void %f(i8* %p) readonly nounwind
|
||||||
call void %f(i8* nocapture %p)
|
call void %f(i8* nocapture %p)
|
||||||
|
@ -213,19 +216,22 @@ define void @test6_2(i8* %x6_2, i8* %y6_2, i8* %z6_2) {
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; EITHER: define void @test_cmpxchg(i32* nocapture %p)
|
; FNATTR: define void @test_cmpxchg(i32* nocapture %p)
|
||||||
|
; ATTRIBUTOR: define void @test_cmpxchg(i32* nocapture nonnull dereferenceable(4) %p)
|
||||||
define void @test_cmpxchg(i32* %p) {
|
define void @test_cmpxchg(i32* %p) {
|
||||||
cmpxchg i32* %p, i32 0, i32 1 acquire monotonic
|
cmpxchg i32* %p, i32 0, i32 1 acquire monotonic
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; EITHER: define void @test_cmpxchg_ptr(i32** nocapture %p, i32* %q)
|
; FNATTR: define void @test_cmpxchg_ptr(i32** nocapture %p, i32* %q)
|
||||||
|
; ATTRIBUTOR: define void @test_cmpxchg_ptr(i32** nocapture nonnull dereferenceable(8) %p, i32* %q)
|
||||||
define void @test_cmpxchg_ptr(i32** %p, i32* %q) {
|
define void @test_cmpxchg_ptr(i32** %p, i32* %q) {
|
||||||
cmpxchg i32** %p, i32* null, i32* %q acquire monotonic
|
cmpxchg i32** %p, i32* null, i32* %q acquire monotonic
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; EITHER: define void @test_atomicrmw(i32* nocapture %p)
|
; FNATTR: define void @test_atomicrmw(i32* nocapture %p)
|
||||||
|
; ATTRIBUTOR: define void @test_atomicrmw(i32* nocapture nonnull dereferenceable(4) %p)
|
||||||
define void @test_atomicrmw(i32* %p) {
|
define void @test_atomicrmw(i32* %p) {
|
||||||
atomicrmw add i32* %p, i32 1 seq_cst
|
atomicrmw add i32* %p, i32 1 seq_cst
|
||||||
ret void
|
ret void
|
||||||
|
|
|
@ -236,6 +236,104 @@ define void @f15(i8* %arg) {
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare void @fun0() #1
|
||||||
|
declare void @fun1(i8*) #1
|
||||||
|
declare void @fun2(i8*, i8*) #1
|
||||||
|
declare void @fun3(i8*, i8*, i8*) #1
|
||||||
|
; TEST 16 simple path test
|
||||||
|
; if(..)
|
||||||
|
; fun2(nonnull %a, nonnull %b)
|
||||||
|
; else
|
||||||
|
; fun2(nonnull %a, %b)
|
||||||
|
; We can say that %a is nonnull but %b is not.
|
||||||
|
define void @f16(i8* %a, i8 * %b, i8 %c) {
|
||||||
|
; FIXME: missing nonnull on %a
|
||||||
|
; ATTRIBUTOR: define void @f16(i8* %a, i8* %b, i8 %c)
|
||||||
|
%cmp = icmp eq i8 %c, 0
|
||||||
|
br i1 %cmp, label %if.then, label %if.else
|
||||||
|
if.then:
|
||||||
|
tail call void @fun2(i8* nonnull %a, i8* nonnull %b)
|
||||||
|
ret void
|
||||||
|
if.else:
|
||||||
|
tail call void @fun2(i8* nonnull %a, i8* %b)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
; TEST 17 explore child BB test
|
||||||
|
; if(..)
|
||||||
|
; ... (willreturn & nounwind)
|
||||||
|
; else
|
||||||
|
; ... (willreturn & nounwind)
|
||||||
|
; fun1(nonnull %a)
|
||||||
|
; We can say that %a is nonnull
|
||||||
|
define void @f17(i8* %a, i8 %c) {
|
||||||
|
; FIXME: missing nonnull on %a
|
||||||
|
; ATTRIBUTOR: define void @f17(i8* %a, i8 %c)
|
||||||
|
%cmp = icmp eq i8 %c, 0
|
||||||
|
br i1 %cmp, label %if.then, label %if.else
|
||||||
|
if.then:
|
||||||
|
tail call void @fun0()
|
||||||
|
br label %cont
|
||||||
|
if.else:
|
||||||
|
tail call void @fun0()
|
||||||
|
br label %cont
|
||||||
|
cont:
|
||||||
|
tail call void @fun1(i8* nonnull %a)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
; TEST 18 More complex test
|
||||||
|
; if(..)
|
||||||
|
; ... (willreturn & nounwind)
|
||||||
|
; else
|
||||||
|
; ... (willreturn & nounwind)
|
||||||
|
; if(..)
|
||||||
|
; ... (willreturn & nounwind)
|
||||||
|
; else
|
||||||
|
; ... (willreturn & nounwind)
|
||||||
|
; fun1(nonnull %a)
|
||||||
|
|
||||||
|
define void @f18(i8* %a, i8* %b, i8 %c) {
|
||||||
|
; FIXME: missing nonnull on %a
|
||||||
|
; ATTRIBUTOR: define void @f18(i8* %a, i8* %b, i8 %c)
|
||||||
|
%cmp1 = icmp eq i8 %c, 0
|
||||||
|
br i1 %cmp1, label %if.then, label %if.else
|
||||||
|
if.then:
|
||||||
|
tail call void @fun0()
|
||||||
|
br label %cont
|
||||||
|
if.else:
|
||||||
|
tail call void @fun0()
|
||||||
|
br label %cont
|
||||||
|
cont:
|
||||||
|
%cmp2 = icmp eq i8 %c, 1
|
||||||
|
br i1 %cmp2, label %cont.then, label %cont.else
|
||||||
|
cont.then:
|
||||||
|
tail call void @fun1(i8* nonnull %b)
|
||||||
|
br label %cont2
|
||||||
|
cont.else:
|
||||||
|
tail call void @fun0()
|
||||||
|
br label %cont2
|
||||||
|
cont2:
|
||||||
|
tail call void @fun1(i8* nonnull %a)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; TEST 19: Loop
|
||||||
|
|
||||||
|
define void @f19(i8* %a, i8* %b, i8 %c) {
|
||||||
|
; FIXME: missing nonnull on %b
|
||||||
|
; ATTRIBUTOR: define void @f19(i8* %a, i8* %b, i8 %c)
|
||||||
|
br label %loop.header
|
||||||
|
loop.header:
|
||||||
|
%cmp2 = icmp eq i8 %c, 0
|
||||||
|
br i1 %cmp2, label %loop.body, label %loop.exit
|
||||||
|
loop.body:
|
||||||
|
tail call void @fun1(i8* nonnull %b)
|
||||||
|
tail call void @fun1(i8* nonnull %a)
|
||||||
|
br label %loop.header
|
||||||
|
loop.exit:
|
||||||
|
tail call void @fun1(i8* nonnull %b)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
; Test propagation of nonnull callsite args back to caller.
|
; Test propagation of nonnull callsite args back to caller.
|
||||||
|
|
||||||
declare void @use1(i8* %x)
|
declare void @use1(i8* %x)
|
||||||
|
@ -268,14 +366,9 @@ define void @parent2(i8* %a, i8* %b, i8* %c) {
|
||||||
; FNATTR-NEXT: call void @use3nonnull(i8* %b, i8* %c, i8* %a)
|
; FNATTR-NEXT: call void @use3nonnull(i8* %b, i8* %c, i8* %a)
|
||||||
; FNATTR-NEXT: call void @use3(i8* %c, i8* %a, i8* %b)
|
; FNATTR-NEXT: call void @use3(i8* %c, i8* %a, i8* %b)
|
||||||
|
|
||||||
; FIXME: missing "nonnull", it should be
|
; ATTRIBUTOR-LABEL: @parent2(i8* nonnull %a, i8* nonnull %b, i8* nonnull %c)
|
||||||
; @parent2(i8* nonnull %a, i8* nonnull %b, i8* nonnull %c)
|
|
||||||
; call void @use3nonnull(i8* nonnull %b, i8* nonnull %c, i8* nonnull %a)
|
|
||||||
; call void @use3(i8* nonnull %c, i8* nonnull %a, i8* nonnull %b)
|
|
||||||
|
|
||||||
; ATTRIBUTOR-LABEL: @parent2(i8* %a, i8* %b, i8* %c)
|
|
||||||
; ATTRIBUTOR-NEXT: call void @use3nonnull(i8* nonnull %b, i8* nonnull %c, i8* nonnull %a)
|
; ATTRIBUTOR-NEXT: call void @use3nonnull(i8* nonnull %b, i8* nonnull %c, i8* nonnull %a)
|
||||||
; ATTRIBUTOR-NEXT: call void @use3(i8* %c, i8* %a, i8* %b)
|
; ATTRIBUTOR-NEXT: call void @use3(i8* nonnull %c, i8* nonnull %a, i8* nonnull %b)
|
||||||
|
|
||||||
; BOTH-NEXT: ret void
|
; BOTH-NEXT: ret void
|
||||||
call void @use3nonnull(i8* %b, i8* %c, i8* %a)
|
call void @use3nonnull(i8* %b, i8* %c, i8* %a)
|
||||||
|
@ -290,13 +383,9 @@ define void @parent3(i8* %a, i8* %b, i8* %c) {
|
||||||
; FNATTR-NEXT: call void @use1nonnull(i8* %a)
|
; FNATTR-NEXT: call void @use1nonnull(i8* %a)
|
||||||
; FNATTR-NEXT: call void @use3(i8* %c, i8* %b, i8* %a)
|
; FNATTR-NEXT: call void @use3(i8* %c, i8* %b, i8* %a)
|
||||||
|
|
||||||
; FIXME: missing "nonnull", it should be,
|
; ATTRIBUTOR-LABEL: @parent3(i8* nonnull %a, i8* %b, i8* %c)
|
||||||
; @parent3(i8* nonnull %a, i8* %b, i8* %c)
|
|
||||||
; call void @use1nonnull(i8* nonnull %a)
|
|
||||||
; call void @use3(i8* %c, i8* %b, i8* nonnull %a)
|
|
||||||
; ATTRIBUTOR-LABEL: @parent3(i8* %a, i8* %b, i8* %c)
|
|
||||||
; ATTRIBUTOR-NEXT: call void @use1nonnull(i8* nonnull %a)
|
; ATTRIBUTOR-NEXT: call void @use1nonnull(i8* nonnull %a)
|
||||||
; ATTRIBUTOR-NEXT: call void @use3(i8* %c, i8* %b, i8* %a)
|
; ATTRIBUTOR-NEXT: call void @use3(i8* %c, i8* %b, i8* nonnull %a)
|
||||||
|
|
||||||
; BOTH-NEXT: ret void
|
; BOTH-NEXT: ret void
|
||||||
|
|
||||||
|
@ -313,16 +402,10 @@ define void @parent4(i8* %a, i8* %b, i8* %c) {
|
||||||
; CHECK-NEXT: call void @use2(i8* %a, i8* %c)
|
; CHECK-NEXT: call void @use2(i8* %a, i8* %c)
|
||||||
; CHECK-NEXT: call void @use1(i8* %b)
|
; CHECK-NEXT: call void @use1(i8* %b)
|
||||||
|
|
||||||
; FIXME : missing "nonnull", it should be
|
; ATTRIBUTOR-LABEL: @parent4(i8* %a, i8* nonnull %b, i8* nonnull %c)
|
||||||
; @parent4(i8* %a, i8* nonnull %b, i8* nonnull %c)
|
|
||||||
; call void @use2nonnull(i8* nonnull %c, i8* nonull %b)
|
|
||||||
; call void @use2(i8* %a, i8* nonnull %c)
|
|
||||||
; call void @use1(i8* nonnull %b)
|
|
||||||
|
|
||||||
; ATTRIBUTOR-LABEL: @parent4(i8* %a, i8* %b, i8* %c)
|
|
||||||
; ATTRIBUTOR-NEXT: call void @use2nonnull(i8* nonnull %c, i8* nonnull %b)
|
; ATTRIBUTOR-NEXT: call void @use2nonnull(i8* nonnull %c, i8* nonnull %b)
|
||||||
; ATTRIBUTOR-NEXT: call void @use2(i8* %a, i8* %c)
|
; ATTRIBUTOR-NEXT: call void @use2(i8* %a, i8* nonnull %c)
|
||||||
; ATTRIBUTOR-NEXT: call void @use1(i8* %b)
|
; ATTRIBUTOR-NEXT: call void @use1(i8* nonnull %b)
|
||||||
|
|
||||||
; BOTH: ret void
|
; BOTH: ret void
|
||||||
|
|
||||||
|
@ -359,8 +442,7 @@ f:
|
||||||
|
|
||||||
define i8 @parent6(i8* %a, i8* %b) {
|
define i8 @parent6(i8* %a, i8* %b) {
|
||||||
; FNATTR-LABEL: @parent6(i8* nonnull %a, i8* %b)
|
; FNATTR-LABEL: @parent6(i8* nonnull %a, i8* %b)
|
||||||
; FIXME: missing "nonnull"
|
; ATTRIBUTOR-LABEL: @parent6(i8* nonnull %a, i8* %b)
|
||||||
; ATTRIBUTOR-LABEL: @parent6(i8* %a, i8* %b)
|
|
||||||
; BOTH-NEXT: [[C:%.*]] = load volatile i8, i8* %b
|
; BOTH-NEXT: [[C:%.*]] = load volatile i8, i8* %b
|
||||||
; FNATTR-NEXT: call void @use1nonnull(i8* %a)
|
; FNATTR-NEXT: call void @use1nonnull(i8* %a)
|
||||||
; ATTRIBUTOR-NEXT: call void @use1nonnull(i8* nonnull %a)
|
; ATTRIBUTOR-NEXT: call void @use1nonnull(i8* nonnull %a)
|
||||||
|
@ -378,14 +460,9 @@ define i8 @parent7(i8* %a) {
|
||||||
; FNATTR-NEXT: [[RET:%.*]] = call i8 @use1safecall(i8* %a)
|
; FNATTR-NEXT: [[RET:%.*]] = call i8 @use1safecall(i8* %a)
|
||||||
; FNATTR-NEXT: call void @use1nonnull(i8* %a)
|
; FNATTR-NEXT: call void @use1nonnull(i8* %a)
|
||||||
|
|
||||||
; FIXME : missing "nonnull", it should be
|
|
||||||
; @parent7(i8* nonnull %a)
|
|
||||||
; [[RET:%.*]] = call i8 @use1safecall(i8* nonnull %a)
|
|
||||||
; call void @use1nonnull(i8* nonnull %a)
|
|
||||||
; ret i8 [[RET]]
|
|
||||||
|
|
||||||
; ATTRIBUTOR-LABEL: @parent7(i8* %a)
|
; ATTRIBUTOR-LABEL: @parent7(i8* nonnull %a)
|
||||||
; ATTRIBUTOR-NEXT: [[RET:%.*]] = call i8 @use1safecall(i8* %a)
|
; ATTRIBUTOR-NEXT: [[RET:%.*]] = call i8 @use1safecall(i8* nonnull %a)
|
||||||
; ATTRIBUTOR-NEXT: call void @use1nonnull(i8* nonnull %a)
|
; ATTRIBUTOR-NEXT: call void @use1nonnull(i8* nonnull %a)
|
||||||
|
|
||||||
; BOTH-NEXT: ret i8 [[RET]]
|
; BOTH-NEXT: ret i8 [[RET]]
|
||||||
|
@ -400,9 +477,7 @@ define i8 @parent7(i8* %a) {
|
||||||
declare i32 @esfp(...)
|
declare i32 @esfp(...)
|
||||||
|
|
||||||
define i1 @parent8(i8* %a, i8* %bogus1, i8* %b) personality i8* bitcast (i32 (...)* @esfp to i8*){
|
define i1 @parent8(i8* %a, i8* %bogus1, i8* %b) personality i8* bitcast (i32 (...)* @esfp to i8*){
|
||||||
; FNATTR-LABEL: @parent8(i8* nonnull %a, i8* nocapture readnone %bogus1, i8* nonnull %b)
|
; BOTH-LABEL: @parent8(i8* nonnull %a, i8* nocapture readnone %bogus1, i8* nonnull %b)
|
||||||
; FIXME : missing "nonnull", it should be @parent8(i8* nonnull %a, i8* %bogus1, i8* nonnull %b)
|
|
||||||
; ATTRIBUTOR-LABEL: @parent8(i8* %a, i8* nocapture readnone %bogus1, i8* %b)
|
|
||||||
; BOTH-NEXT: entry:
|
; BOTH-NEXT: entry:
|
||||||
; FNATTR-NEXT: invoke void @use2nonnull(i8* %a, i8* %b)
|
; FNATTR-NEXT: invoke void @use2nonnull(i8* %a, i8* %b)
|
||||||
; ATTRIBUTOR-NEXT: invoke void @use2nonnull(i8* nonnull %a, i8* nonnull %b)
|
; ATTRIBUTOR-NEXT: invoke void @use2nonnull(i8* nonnull %a, i8* nonnull %b)
|
||||||
|
@ -470,4 +545,6 @@ define weak_odr void @weak_caller(i32* nonnull %a) {
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
attributes #0 = { "null-pointer-is-valid"="true" }
|
attributes #0 = { "null-pointer-is-valid"="true" }
|
||||||
|
attributes #1 = { nounwind willreturn}
|
||||||
|
|
|
@ -130,8 +130,15 @@ define linkonce_odr i32 @leaf_redefinable() {
|
||||||
|
|
||||||
; Call through a function pointer
|
; Call through a function pointer
|
||||||
; ATTRIBUTOR-NOT: Function Attrs
|
; ATTRIBUTOR-NOT: Function Attrs
|
||||||
; ATTRIBUTOR: define i32 @eval_func(i32 (i32)* nocapture %0, i32 %1)
|
; ATTRIBUTOR: define i32 @eval_func1(i32 (i32)* nocapture nonnull %0, i32 %1)
|
||||||
define i32 @eval_func(i32 (i32)* , i32) local_unnamed_addr {
|
define i32 @eval_func1(i32 (i32)* , i32) local_unnamed_addr {
|
||||||
|
%3 = tail call i32 %0(i32 %1) #2
|
||||||
|
ret i32 %3
|
||||||
|
}
|
||||||
|
|
||||||
|
; ATTRIBUTOR-NOT: Function Attrs
|
||||||
|
; ATTRIBUTOR: define i32 @eval_func2(i32 (i32)* nocapture %0, i32 %1)
|
||||||
|
define i32 @eval_func2(i32 (i32)* , i32) local_unnamed_addr "null-pointer-is-valid"="true"{
|
||||||
%3 = tail call i32 %0(i32 %1) #2
|
%3 = tail call i32 %0(i32 %1) #2
|
||||||
ret i32 %3
|
ret i32 %3
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ entry:
|
||||||
; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
|
; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
|
||||||
; FNATTR-NEXT: define i32 @load_monotonic(i32* nocapture readonly %0)
|
; FNATTR-NEXT: define i32 @load_monotonic(i32* nocapture readonly %0)
|
||||||
; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind uwtable
|
; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind uwtable
|
||||||
; ATTRIBUTOR-NEXT: define i32 @load_monotonic(i32* nocapture readonly %0)
|
; ATTRIBUTOR-NEXT: define i32 @load_monotonic(i32* nocapture nonnull readonly dereferenceable(4) %0)
|
||||||
define i32 @load_monotonic(i32* nocapture readonly %0) norecurse nounwind uwtable {
|
define i32 @load_monotonic(i32* nocapture readonly %0) norecurse nounwind uwtable {
|
||||||
%2 = load atomic i32, i32* %0 monotonic, align 4
|
%2 = load atomic i32, i32* %0 monotonic, align 4
|
||||||
ret i32 %2
|
ret i32 %2
|
||||||
|
@ -61,7 +61,7 @@ define i32 @load_monotonic(i32* nocapture readonly %0) norecurse nounwind uwtabl
|
||||||
; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
|
; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
|
||||||
; FNATTR-NEXT: define void @store_monotonic(i32* nocapture %0)
|
; FNATTR-NEXT: define void @store_monotonic(i32* nocapture %0)
|
||||||
; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind uwtable
|
; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind uwtable
|
||||||
; ATTRIBUTOR-NEXT: define void @store_monotonic(i32* nocapture writeonly %0)
|
; ATTRIBUTOR-NEXT: define void @store_monotonic(i32* nocapture nonnull writeonly dereferenceable(4) %0)
|
||||||
define void @store_monotonic(i32* nocapture %0) norecurse nounwind uwtable {
|
define void @store_monotonic(i32* nocapture %0) norecurse nounwind uwtable {
|
||||||
store atomic i32 10, i32* %0 monotonic, align 4
|
store atomic i32 10, i32* %0 monotonic, align 4
|
||||||
ret void
|
ret void
|
||||||
|
@ -78,7 +78,7 @@ define void @store_monotonic(i32* nocapture %0) norecurse nounwind uwtable {
|
||||||
; FNATTR-NEXT: define i32 @load_acquire(i32* nocapture readonly %0)
|
; FNATTR-NEXT: define i32 @load_acquire(i32* nocapture readonly %0)
|
||||||
; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable
|
; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable
|
||||||
; ATTRIBUTOR-NOT: nosync
|
; ATTRIBUTOR-NOT: nosync
|
||||||
; ATTRIBUTOR-NEXT: define i32 @load_acquire(i32* nocapture readonly %0)
|
; ATTRIBUTOR-NEXT: define i32 @load_acquire(i32* nocapture nonnull readonly dereferenceable(4) %0)
|
||||||
define i32 @load_acquire(i32* nocapture readonly %0) norecurse nounwind uwtable {
|
define i32 @load_acquire(i32* nocapture readonly %0) norecurse nounwind uwtable {
|
||||||
%2 = load atomic i32, i32* %0 acquire, align 4
|
%2 = load atomic i32, i32* %0 acquire, align 4
|
||||||
ret i32 %2
|
ret i32 %2
|
||||||
|
@ -224,7 +224,8 @@ define void @scc2(i32* %0) noinline nounwind uwtable {
|
||||||
; FNATTR: Function Attrs: nofree norecurse nounwind
|
; FNATTR: Function Attrs: nofree norecurse nounwind
|
||||||
; FNATTR-NEXT: define void @foo1(i32* nocapture %0, %"struct.std::atomic"* nocapture %1)
|
; FNATTR-NEXT: define void @foo1(i32* nocapture %0, %"struct.std::atomic"* nocapture %1)
|
||||||
; ATTRIBUTOR-NOT: nosync
|
; ATTRIBUTOR-NOT: nosync
|
||||||
; ATTRIBUTOR: define void @foo1(i32* nocapture writeonly %0, %"struct.std::atomic"* nocapture writeonly %1)
|
; ATTRIBUTOR: define void @foo1(i32* nocapture nonnull writeonly dereferenceable(4) %0, %"struct.std::atomic"* nocapture writeonly %1)
|
||||||
|
|
||||||
define void @foo1(i32* %0, %"struct.std::atomic"* %1) {
|
define void @foo1(i32* %0, %"struct.std::atomic"* %1) {
|
||||||
store i32 100, i32* %0, align 4
|
store i32 100, i32* %0, align 4
|
||||||
fence release
|
fence release
|
||||||
|
@ -255,8 +256,9 @@ define void @bar(i32* %0, %"struct.std::atomic"* %1) {
|
||||||
; TEST 13 - Fence syncscope("singlethread") seq_cst
|
; TEST 13 - Fence syncscope("singlethread") seq_cst
|
||||||
; FNATTR: Function Attrs: nofree norecurse nounwind
|
; FNATTR: Function Attrs: nofree norecurse nounwind
|
||||||
; FNATTR-NEXT: define void @foo1_singlethread(i32* nocapture %0, %"struct.std::atomic"* nocapture %1)
|
; FNATTR-NEXT: define void @foo1_singlethread(i32* nocapture %0, %"struct.std::atomic"* nocapture %1)
|
||||||
; ATTRIBUTOR: Function Attrs: nofree nosync
|
; ATTRIBUTOR: Function Attrs: nofree nosync nounwind willreturn
|
||||||
; ATTRIBUTOR: define void @foo1_singlethread(i32* nocapture writeonly %0, %"struct.std::atomic"* nocapture writeonly %1)
|
; ATTRIBUTOR: define void @foo1_singlethread(i32* nocapture nonnull writeonly dereferenceable(4) %0, %"struct.std::atomic"* nocapture writeonly %1)
|
||||||
|
|
||||||
define void @foo1_singlethread(i32* %0, %"struct.std::atomic"* %1) {
|
define void @foo1_singlethread(i32* %0, %"struct.std::atomic"* %1) {
|
||||||
store i32 100, i32* %0, align 4
|
store i32 100, i32* %0, align 4
|
||||||
fence syncscope("singlethread") release
|
fence syncscope("singlethread") release
|
||||||
|
@ -267,7 +269,7 @@ define void @foo1_singlethread(i32* %0, %"struct.std::atomic"* %1) {
|
||||||
|
|
||||||
; FNATTR: Function Attrs: nofree norecurse nounwind
|
; FNATTR: Function Attrs: nofree norecurse nounwind
|
||||||
; FNATTR-NEXT: define void @bar_singlethread(i32* nocapture readnone %0, %"struct.std::atomic"* nocapture readonly %1)
|
; FNATTR-NEXT: define void @bar_singlethread(i32* nocapture readnone %0, %"struct.std::atomic"* nocapture readonly %1)
|
||||||
; ATTRIBUTOR: Function Attrs: nofree nosync
|
; ATTRIBUTOR: Function Attrs: nofree nosync nounwind
|
||||||
; ATTRIBUTOR: define void @bar_singlethread(i32* nocapture readnone %0, %"struct.std::atomic"* nocapture readonly %1)
|
; ATTRIBUTOR: define void @bar_singlethread(i32* nocapture readnone %0, %"struct.std::atomic"* nocapture readonly %1)
|
||||||
define void @bar_singlethread(i32* %0, %"struct.std::atomic"* %1) {
|
define void @bar_singlethread(i32* %0, %"struct.std::atomic"* %1) {
|
||||||
%3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
|
%3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
|
||||||
|
|
|
@ -70,7 +70,7 @@ return: ; preds = %if.end, %if.then
|
||||||
}
|
}
|
||||||
|
|
||||||
; CHECK: Function Attrs: nofree nosync nounwind
|
; CHECK: Function Attrs: nofree nosync nounwind
|
||||||
; CHECK-NEXT: define internal i32* @internal_ret1_rrw(i32* %r0, i32* returned %r1, i32* %w0)
|
; CHECK-NEXT: define internal i32* @internal_ret1_rrw(i32* nonnull dereferenceable(4) %r0, i32* returned %r1, i32* %w0)
|
||||||
define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) {
|
define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) {
|
||||||
entry:
|
entry:
|
||||||
%0 = load i32, i32* %r0, align 4
|
%0 = load i32, i32* %r0, align 4
|
||||||
|
@ -121,7 +121,7 @@ return: ; preds = %if.end, %if.then
|
||||||
}
|
}
|
||||||
|
|
||||||
; CHECK: Function Attrs: nofree nosync nounwind
|
; CHECK: Function Attrs: nofree nosync nounwind
|
||||||
; CHECK-NEXT: define internal i32* @internal_ret1_rw(i32* %r0, i32* returned %w0)
|
; CHECK-NEXT: define internal i32* @internal_ret1_rw(i32* nonnull dereferenceable(4) %r0, i32* returned %w0)
|
||||||
define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) {
|
define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) {
|
||||||
entry:
|
entry:
|
||||||
%0 = load i32, i32* %r0, align 4
|
%0 = load i32, i32* %r0, align 4
|
||||||
|
|
|
@ -39,7 +39,7 @@ define void @test4_2(i8* %p) {
|
||||||
}
|
}
|
||||||
|
|
||||||
; FNATTR: define void @test5(i8** nocapture %p, i8* %q)
|
; FNATTR: define void @test5(i8** nocapture %p, i8* %q)
|
||||||
; ATTRIBUTOR: define void @test5(i8** nocapture writeonly %p, i8* %q)
|
; ATTRIBUTOR: define void @test5(i8** nocapture nonnull writeonly dereferenceable(8) %p, i8* %q)
|
||||||
; Missed optz'n: we could make %q readnone, but don't break test6!
|
; Missed optz'n: we could make %q readnone, but don't break test6!
|
||||||
define void @test5(i8** %p, i8* %q) {
|
define void @test5(i8** %p, i8* %q) {
|
||||||
store i8* %q, i8** %p
|
store i8* %q, i8** %p
|
||||||
|
@ -48,7 +48,7 @@ define void @test5(i8** %p, i8* %q) {
|
||||||
|
|
||||||
declare void @test6_1()
|
declare void @test6_1()
|
||||||
; FNATTR: define void @test6_2(i8** nocapture %p, i8* %q)
|
; FNATTR: define void @test6_2(i8** nocapture %p, i8* %q)
|
||||||
; ATTRIBUTOR: define void @test6_2(i8** nocapture writeonly %p, i8* %q)
|
; ATTRIBUTOR: define void @test6_2(i8** nocapture nonnull writeonly dereferenceable(8) %p, i8* %q)
|
||||||
; This is not a missed optz'n.
|
; This is not a missed optz'n.
|
||||||
define void @test6_2(i8** %p, i8* %q) {
|
define void @test6_2(i8** %p, i8* %q) {
|
||||||
store i8* %q, i8** %p
|
store i8* %q, i8** %p
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
; RUN: opt < %s -inferattrs -S | FileCheck %s
|
; RUN: opt < %s -inferattrs -S | FileCheck %s
|
||||||
|
; RUN: opt < %s -attributor --attributor-disable=false -S | FileCheck %s --check-prefix=ATTRIBUTOR
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
; Determine dereference-ability before unused loads get deleted:
|
; Determine dereference-ability before unused loads get deleted:
|
||||||
; https://bugs.llvm.org/show_bug.cgi?id=21780
|
; https://bugs.llvm.org/show_bug.cgi?id=21780
|
||||||
|
|
||||||
define <4 x double> @PR21780(double* %ptr) {
|
define <4 x double> @PR21780(double* %ptr) {
|
||||||
; CHECK-LABEL: @PR21780(double* %ptr)
|
; CHECK-LABEL: @PR21780(double* %ptr)
|
||||||
|
; FIXME: this should be @PR21780(double* nonnull dereferenceable(32) %ptr)
|
||||||
|
; trakcing use of GEP in Attributor would fix this problem.
|
||||||
|
; ATTRIBUTOR-LABEL: @PR21780(double* nocapture nonnull readonly dereferenceable(8) %ptr)
|
||||||
|
|
||||||
; GEP of index 0 is simplified away.
|
; GEP of index 0 is simplified away.
|
||||||
%arrayidx1 = getelementptr inbounds double, double* %ptr, i64 1
|
%arrayidx1 = getelementptr inbounds double, double* %ptr, i64 1
|
||||||
%arrayidx2 = getelementptr inbounds double, double* %ptr, i64 2
|
%arrayidx2 = getelementptr inbounds double, double* %ptr, i64 2
|
||||||
|
@ -23,6 +30,43 @@ define <4 x double> @PR21780(double* %ptr) {
|
||||||
ret <4 x double> %shuffle
|
ret <4 x double> %shuffle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
define double @PR21780_only_access3_with_inbounds(double* %ptr) {
|
||||||
|
; CHECK-LABEL: @PR21780_only_access3_with_inbounds(double* %ptr)
|
||||||
|
; FIXME: this should be @PR21780_only_access3_with_inbounds(double* nonnull dereferenceable(32) %ptr)
|
||||||
|
; trakcing use of GEP in Attributor would fix this problem.
|
||||||
|
; ATTRIBUTOR-LABEL: @PR21780_only_access3_with_inbounds(double* nocapture readonly %ptr)
|
||||||
|
|
||||||
|
%arrayidx3 = getelementptr inbounds double, double* %ptr, i64 3
|
||||||
|
%t3 = load double, double* %arrayidx3, align 8
|
||||||
|
ret double %t3
|
||||||
|
}
|
||||||
|
|
||||||
|
define double @PR21780_only_access3_without_inbounds(double* %ptr) {
|
||||||
|
; CHECK-LABEL: @PR21780_only_access3_without_inbounds(double* %ptr)
|
||||||
|
; ATTRIBUTOR-LABEL: @PR21780_only_access3_without_inbounds(double* nocapture readonly %ptr)
|
||||||
|
%arrayidx3 = getelementptr double, double* %ptr, i64 3
|
||||||
|
%t3 = load double, double* %arrayidx3, align 8
|
||||||
|
ret double %t3
|
||||||
|
}
|
||||||
|
|
||||||
|
define double @PR21780_without_inbounds(double* %ptr) {
|
||||||
|
; CHECK-LABEL: @PR21780_without_inbounds(double* %ptr)
|
||||||
|
; FIXME: this should be @PR21780_without_inbounds(double* nonnull dereferenceable(32) %ptr)
|
||||||
|
; ATTRIBUTOR-LABEL: @PR21780_without_inbounds(double* nocapture nonnull readonly dereferenceable(8) %ptr)
|
||||||
|
|
||||||
|
%arrayidx1 = getelementptr double, double* %ptr, i64 1
|
||||||
|
%arrayidx2 = getelementptr double, double* %ptr, i64 2
|
||||||
|
%arrayidx3 = getelementptr double, double* %ptr, i64 3
|
||||||
|
|
||||||
|
%t0 = load double, double* %ptr, align 8
|
||||||
|
%t1 = load double, double* %arrayidx1, align 8
|
||||||
|
%t2 = load double, double* %arrayidx2, align 8
|
||||||
|
%t3 = load double, double* %arrayidx3, align 8
|
||||||
|
|
||||||
|
ret double %t3
|
||||||
|
}
|
||||||
|
|
||||||
; Unsimplified, but still valid. Also, throw in some bogus arguments.
|
; Unsimplified, but still valid. Also, throw in some bogus arguments.
|
||||||
|
|
||||||
define void @gep0(i8* %unused, i8* %other, i8* %ptr) {
|
define void @gep0(i8* %unused, i8* %other, i8* %ptr) {
|
||||||
|
|
Loading…
Reference in New Issue