From e9ce3e9f6730dd09db206348ed856d9bb8f240fb Mon Sep 17 00:00:00 2001 From: Hideto Ueno Date: Tue, 8 Oct 2019 15:25:56 +0000 Subject: [PATCH] [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 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@374063 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Transforms/IPO/Attributor.h | 16 +- lib/Transforms/IPO/Attributor.cpp | 233 ++++++++++++++++-- test/Transforms/FunctionAttrs/align.ll | 4 +- .../Transforms/FunctionAttrs/arg_nocapture.ll | 4 +- test/Transforms/FunctionAttrs/arg_returned.ll | 4 +- test/Transforms/FunctionAttrs/callbacks.ll | 6 +- .../FunctionAttrs/dereferenceable.ll | 93 ++++++- .../FunctionAttrs/internal-noalias.ll | 5 +- test/Transforms/FunctionAttrs/liveness.ll | 2 +- .../FunctionAttrs/noalias_returned.ll | 2 +- test/Transforms/FunctionAttrs/nocapture.ll | 16 +- test/Transforms/FunctionAttrs/nonnull.ll | 145 ++++++++--- test/Transforms/FunctionAttrs/norecurse.ll | 11 +- test/Transforms/FunctionAttrs/nosync.ll | 16 +- .../read_write_returned_arguments_scc.ll | 4 +- test/Transforms/FunctionAttrs/readattrs.ll | 4 +- .../InferFunctionAttrs/dereferenceable.ll | 44 ++++ 17 files changed, 519 insertions(+), 90 deletions(-) diff --git a/include/llvm/Transforms/IPO/Attributor.h b/include/llvm/Transforms/IPO/Attributor.h index be41f82d7d0..04ab8f74755 100644 --- a/include/llvm/Transforms/IPO/Attributor.h +++ b/include/llvm/Transforms/IPO/Attributor.h @@ -101,6 +101,7 @@ #include "llvm/ADT/SetVector.h" #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/CallGraph.h" +#include "llvm/Analysis/MustExecute.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/PassManager.h" @@ -595,7 +596,7 @@ private: /// instance down in the abstract attributes. struct InformationCache { InformationCache(const Module &M, AnalysisGetter &AG) - : DL(M.getDataLayout()), AG(AG) { + : DL(M.getDataLayout()), Explorer(/* ExploreInterBlock */ true), AG(AG) { CallGraph *CG = AG.getAnalysis(M); if (!CG) @@ -626,6 +627,11 @@ struct InformationCache { return FuncRWInstsMap[&F]; } + /// Return MustBeExecutedContextExplorer + MustBeExecutedContextExplorer &getMustBeExecutedContextExplorer() { + return Explorer; + } + /// Return TargetLibraryInfo for function \p F. TargetLibraryInfo *getTargetLibraryInfoForFunction(const Function &F) { return AG.getAnalysis(F); @@ -663,6 +669,9 @@ private: /// The datalayout used in the module. const DataLayout &DL; + /// MustBeExecutedContextExplorer + MustBeExecutedContextExplorer Explorer; + /// Getters for analysis. AnalysisGetter &AG; @@ -1714,6 +1723,11 @@ struct AADereferenceable 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 /// dereferenceable(_or_null) globally. bool isAssumedGlobal() const { return GlobalState.getAssumed(); } diff --git a/lib/Transforms/IPO/Attributor.cpp b/lib/Transforms/IPO/Attributor.cpp index 3a1562252f5..4943721c469 100644 --- a/lib/Transforms/IPO/Attributor.cpp +++ b/lib/Transforms/IPO/Attributor.cpp @@ -288,6 +288,35 @@ static bool addIfNotExistent(LLVMContext &Ctx, const Attribute &Attr, llvm_unreachable("Expected enum or string attribute!"); } +static const Value *getPointerOperand(const Instruction *I) { + if (auto *LI = dyn_cast(I)) + if (!LI->isVolatile()) + return LI->getPointerOperand(); + + if (auto *SI = dyn_cast(I)) + if (!SI->isVolatile()) + return SI->getPointerOperand(); + + if (auto *CXI = dyn_cast(I)) + if (!CXI->isVolatile()) + return CXI->getPointerOperand(); + + if (auto *RMWI = dyn_cast(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 HasChanged = ChangeStatus::UNCHANGED; @@ -654,7 +683,8 @@ struct AAArgumentFromCallSiteArguments : public Base { }; /// Helper class for generic replication: function returned -> cs returned. -template +template struct AACallSiteReturnedFromReturned : public Base { 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 +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 NextUses; + + for (const Use *U : Uses) { + if (const Instruction *UserI = dyn_cast(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 Uses; +}; + +template +using AAArgumentFromCallSiteArgumentsAndMustBeExecutedContext = + AAComposeTwoGenericDeduction; + +template +using AACallSiteReturnedFromReturnedAndMustBeExecutedContext = + AAComposeTwoGenericDeduction; + /// -----------------------NoUnwind Function Attribute-------------------------- struct AANoUnwindImpl : AANoUnwind { @@ -1434,6 +1538,46 @@ struct AANoFreeCallSite final : AANoFreeImpl { }; /// ------------------------ 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(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 { AANonNullImpl(const IRPosition &IRP) : AANonNull(IRP) {} @@ -1445,6 +1589,16 @@ struct AANonNullImpl : AANonNull { 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(). const std::string getAsStr() const override { return getAssumed() ? "nonnull" : "may-null"; @@ -1452,12 +1606,14 @@ struct AANonNullImpl : AANonNull { }; /// NonNull attribute for a floating value. -struct AANonNullFloating : AANonNullImpl { - AANonNullFloating(const IRPosition &IRP) : AANonNullImpl(IRP) {} +struct AANonNullFloating + : AAFromMustBeExecutedContext { + using Base = AAFromMustBeExecutedContext; + AANonNullFloating(const IRPosition &IRP) : Base(IRP) {} /// See AbstractAttribute::initialize(...). void initialize(Attributor &A) override { - AANonNullImpl::initialize(A); + Base::initialize(A); if (isAtFixpoint()) return; @@ -1475,6 +1631,10 @@ struct AANonNullFloating : AANonNullImpl { /// See AbstractAttribute::updateImpl(...). ChangeStatus updateImpl(Attributor &A) override { + ChangeStatus Change = Base::updateImpl(A); + if (isKnownNonNull()) + return Change; + const DataLayout &DL = A.getDataLayout(); auto VisitValueCB = [&](Value &V, AAAlign::StateType &T, @@ -1518,9 +1678,12 @@ struct AANonNullReturned final /// NonNull attribute for function argument. struct AANonNullArgument final - : AAArgumentFromCallSiteArguments { + : AAArgumentFromCallSiteArgumentsAndMustBeExecutedContext { AANonNullArgument(const IRPosition &IRP) - : AAArgumentFromCallSiteArguments(IRP) {} + : AAArgumentFromCallSiteArgumentsAndMustBeExecutedContext( + IRP) {} /// See AbstractAttribute::trackStatistics() 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. struct AANonNullCallSiteReturned final - : AACallSiteReturnedFromReturned { + : AACallSiteReturnedFromReturnedAndMustBeExecutedContext { AANonNullCallSiteReturned(const IRPosition &IRP) - : AACallSiteReturnedFromReturned(IRP) {} + : AACallSiteReturnedFromReturnedAndMustBeExecutedContext( + IRP) {} /// See AbstractAttribute::trackStatistics() void trackStatistics() const override { STATS_DECLTRACK_CSRET_ATTR(nonnull) } @@ -2290,6 +2456,16 @@ struct AADereferenceableImpl : AADereferenceable { 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, SmallVectorImpl &Attrs) const override { // TODO: Add *_globally support @@ -2314,12 +2490,16 @@ struct AADereferenceableImpl : AADereferenceable { }; /// Dereferenceable attribute for a floating value. -struct AADereferenceableFloating : AADereferenceableImpl { - AADereferenceableFloating(const IRPosition &IRP) - : AADereferenceableImpl(IRP) {} +struct AADereferenceableFloating + : AAFromMustBeExecutedContext { + using Base = + AAFromMustBeExecutedContext; + AADereferenceableFloating(const IRPosition &IRP) : Base(IRP) {} /// See AbstractAttribute::updateImpl(...). ChangeStatus updateImpl(Attributor &A) override { + ChangeStatus Change = Base::updateImpl(A); + const DataLayout &DL = A.getDataLayout(); auto VisitValueCB = [&](Value &V, DerefState &T, bool Stripped) -> bool { @@ -2378,7 +2558,7 @@ struct AADereferenceableFloating : AADereferenceableImpl { A, getIRPosition(), *this, T, VisitValueCB)) return indicatePessimisticFixpoint(); - return clampStateAndIndicateChange(getState(), T); + return Change | clampStateAndIndicateChange(getState(), T); } /// See AbstractAttribute::trackStatistics() @@ -2403,12 +2583,11 @@ struct AADereferenceableReturned final /// Dereferenceable attribute for an argument struct AADereferenceableArgument final - : AAArgumentFromCallSiteArguments { - AADereferenceableArgument(const IRPosition &IRP) - : AAArgumentFromCallSiteArguments( - IRP) {} + : AAArgumentFromCallSiteArgumentsAndMustBeExecutedContext< + AADereferenceable, AADereferenceableImpl, DerefState> { + using Base = AAArgumentFromCallSiteArgumentsAndMustBeExecutedContext< + AADereferenceable, AADereferenceableImpl, DerefState>; + AADereferenceableArgument(const IRPosition &IRP) : Base(IRP) {} /// See AbstractAttribute::trackStatistics() void trackStatistics() const override { @@ -2428,13 +2607,16 @@ struct AADereferenceableCallSiteArgument final : AADereferenceableFloating { }; /// Dereferenceable attribute deduction for a call site return value. -struct AADereferenceableCallSiteReturned final : AADereferenceableImpl { - AADereferenceableCallSiteReturned(const IRPosition &IRP) - : AADereferenceableImpl(IRP) {} +struct AADereferenceableCallSiteReturned final + : AACallSiteReturnedFromReturnedAndMustBeExecutedContext< + AADereferenceable, AADereferenceableImpl> { + using Base = AACallSiteReturnedFromReturnedAndMustBeExecutedContext< + AADereferenceable, AADereferenceableImpl>; + AADereferenceableCallSiteReturned(const IRPosition &IRP) : Base(IRP) {} /// See AbstractAttribute::initialize(...). void initialize(Attributor &A) override { - AADereferenceableImpl::initialize(A); + Base::initialize(A); Function *F = getAssociatedFunction(); if (!F) indicatePessimisticFixpoint(); @@ -2446,11 +2628,14 @@ struct AADereferenceableCallSiteReturned final : AADereferenceableImpl { // call site specific liveness information and then it makes // sense to specialize attributes for call sites arguments instead of // redirecting requests to the callee argument. + + ChangeStatus Change = Base::updateImpl(A); Function *F = getAssociatedFunction(); const IRPosition &FnPos = IRPosition::returned(*F); auto &FnAA = A.getAAFor(*this, FnPos); - return clampStateAndIndicateChange( - getState(), static_cast(FnAA.getState())); + return Change | + clampStateAndIndicateChange( + getState(), static_cast(FnAA.getState())); } /// See AbstractAttribute::trackStatistics() diff --git a/test/Transforms/FunctionAttrs/align.ll b/test/Transforms/FunctionAttrs/align.ll index 52f94e7a164..f986d31af0a 100644 --- a/test/Transforms/FunctionAttrs/align.ll +++ b/test/Transforms/FunctionAttrs/align.ll @@ -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) ; 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) { ; ATTRIBUTOR: %l = load i32, i32* %p, align 32 %l = load i32, i32* %p @@ -203,7 +203,7 @@ e: ; FIXME: This will work with an upcoming patch (D66618 or similar) ; 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) { ; ATTRIBUTOR: %l = load i32, i32* %p, align 32 %l = load i32, i32* %p diff --git a/test/Transforms/FunctionAttrs/arg_nocapture.ll b/test/Transforms/FunctionAttrs/arg_nocapture.ll index 3b4b054b6e1..18050dba201 100644 --- a/test/Transforms/FunctionAttrs/arg_nocapture.ll +++ b/test/Transforms/FunctionAttrs/arg_nocapture.ll @@ -244,7 +244,8 @@ declare i32 @printf(i8* nocapture, ...) ; } ; ; 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 { entry: 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_through_phi(i32* readnone %b) + declare i32* @unknown() define i32* @ret_arg_or_unknown(i32* %b) #0 { diff --git a/test/Transforms/FunctionAttrs/arg_returned.ll b/test/Transforms/FunctionAttrs/arg_returned.ll index b5c7596222a..e9cf2d81f11 100644 --- a/test/Transforms/FunctionAttrs/arg_returned.ll +++ b/test/Transforms/FunctionAttrs/arg_returned.ll @@ -254,7 +254,7 @@ return: ; preds = %cond.end, %if.then3 ; ; FNATTR: define i32* @rt0(i32* readonly %a) ; 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 { entry: %v = load i32, i32* %a, align 4 @@ -272,7 +272,7 @@ entry: ; ; FNATTR: define noalias i32* @rt1(i32* nocapture readonly %a) ; 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 { entry: %v = load i32, i32* %a, align 4 diff --git a/test/Transforms/FunctionAttrs/callbacks.ll b/test/Transforms/FunctionAttrs/callbacks.ll index 3628bfa9daf..88fe0d037ca 100644 --- a/test/Transforms/FunctionAttrs/callbacks.ll +++ b/test/Transforms/FunctionAttrs/callbacks.ll @@ -1,5 +1,5 @@ ; 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' 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. define void @t0_caller(i32* %a) { -; CHECK: @t0_caller(i32* [[A:%.*]]) +; CHECK-LABEL: @t0_caller( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[B:%.*]] = alloca i32, align 32 ; 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! ; 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) { -; 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: [[PTR_VAL:%.*]] = load i32, i32* [[PTR:%.*]], align 8 ; CHECK-NEXT: store i32 [[PTR_VAL]], i32* [[IS_NOT_NULL:%.*]] diff --git a/test/Transforms/FunctionAttrs/dereferenceable.ll b/test/Transforms/FunctionAttrs/dereferenceable.ll index 1ff1e285dd7..895b0fa1145 100644 --- a/test/Transforms/FunctionAttrs/dereferenceable.ll +++ b/test/Transforms/FunctionAttrs/dereferenceable.ll @@ -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); @@ -111,3 +111,94 @@ for.inc: ; preds = %for.body for.end: ; preds = %for.cond.cleanup 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 +} diff --git a/test/Transforms/FunctionAttrs/internal-noalias.ll b/test/Transforms/FunctionAttrs/internal-noalias.ll index 9eec60b5e1e..9c4158acebf 100644 --- a/test/Transforms/FunctionAttrs/internal-noalias.ll +++ b/test/Transforms/FunctionAttrs/internal-noalias.ll @@ -8,7 +8,7 @@ entry: 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 { entry: @@ -23,7 +23,8 @@ entry: ; FIXME: Should be something like this. ; 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 { entry: diff --git a/test/Transforms/FunctionAttrs/liveness.ll b/test/Transforms/FunctionAttrs/liveness.ll index 080a6bfab84..b75887efb8f 100644 --- a/test/Transforms/FunctionAttrs/liveness.ll +++ b/test/Transforms/FunctionAttrs/liveness.ll @@ -40,7 +40,7 @@ define i32 @volatile_load(i32*) norecurse nounwind uwtable { } ; 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 { %2 = load i32, i32* %0, align 4 ret i32 %2 diff --git a/test/Transforms/FunctionAttrs/noalias_returned.ll b/test/Transforms/FunctionAttrs/noalias_returned.ll index 6aaf88ccf8d..dab08bca3d9 100644 --- a/test/Transforms/FunctionAttrs/noalias_returned.ll +++ b/test/Transforms/FunctionAttrs/noalias_returned.ll @@ -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. diff --git a/test/Transforms/FunctionAttrs/nocapture.ll b/test/Transforms/FunctionAttrs/nocapture.ll index cef45832592..d572e94aa3d 100644 --- a/test/Transforms/FunctionAttrs/nocapture.ll +++ b/test/Transforms/FunctionAttrs/nocapture.ll @@ -120,7 +120,9 @@ define void @nc2(i32* %p, i32* %q) { 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) { call void %p() ret void @@ -133,7 +135,8 @@ define void @nc4(i8* %p) { 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) { call void %f(i8* %p) readonly nounwind 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 } -; 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) { cmpxchg i32* %p, i32 0, i32 1 acquire monotonic 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) { cmpxchg i32** %p, i32* null, i32* %q acquire monotonic 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) { atomicrmw add i32* %p, i32 1 seq_cst ret void diff --git a/test/Transforms/FunctionAttrs/nonnull.ll b/test/Transforms/FunctionAttrs/nonnull.ll index 73517b7c1d6..63b2380a7fc 100644 --- a/test/Transforms/FunctionAttrs/nonnull.ll +++ b/test/Transforms/FunctionAttrs/nonnull.ll @@ -236,6 +236,104 @@ define void @f15(i8* %arg) { 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. 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 @use3(i8* %c, i8* %a, i8* %b) -; FIXME: missing "nonnull", it should be -; @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-LABEL: @parent2(i8* nonnull %a, i8* nonnull %b, i8* nonnull %c) ; 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 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 @use3(i8* %c, i8* %b, i8* %a) -; FIXME: missing "nonnull", it should be, -; @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-LABEL: @parent3(i8* nonnull %a, i8* %b, i8* %c) ; 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 @@ -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 @use1(i8* %b) -; FIXME : missing "nonnull", it should be -; @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-LABEL: @parent4(i8* %a, i8* nonnull %b, i8* nonnull %c) ; ATTRIBUTOR-NEXT: call void @use2nonnull(i8* nonnull %c, i8* nonnull %b) -; ATTRIBUTOR-NEXT: call void @use2(i8* %a, i8* %c) -; ATTRIBUTOR-NEXT: call void @use1(i8* %b) +; ATTRIBUTOR-NEXT: call void @use2(i8* %a, i8* nonnull %c) +; ATTRIBUTOR-NEXT: call void @use1(i8* nonnull %b) ; BOTH: ret void @@ -359,8 +442,7 @@ f: define i8 @parent6(i8* %a, i8* %b) { ; FNATTR-LABEL: @parent6(i8* nonnull %a, i8* %b) -; FIXME: missing "nonnull" -; ATTRIBUTOR-LABEL: @parent6(i8* %a, i8* %b) +; ATTRIBUTOR-LABEL: @parent6(i8* nonnull %a, i8* %b) ; BOTH-NEXT: [[C:%.*]] = load volatile i8, i8* %b ; FNATTR-NEXT: call void @use1nonnull(i8* %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: 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-NEXT: [[RET:%.*]] = call i8 @use1safecall(i8* %a) +; ATTRIBUTOR-LABEL: @parent7(i8* nonnull %a) +; ATTRIBUTOR-NEXT: [[RET:%.*]] = call i8 @use1safecall(i8* nonnull %a) ; ATTRIBUTOR-NEXT: call void @use1nonnull(i8* nonnull %a) ; BOTH-NEXT: ret i8 [[RET]] @@ -400,9 +477,7 @@ define i8 @parent7(i8* %a) { declare i32 @esfp(...) 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) -; 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-LABEL: @parent8(i8* nonnull %a, i8* nocapture readnone %bogus1, i8* nonnull %b) ; BOTH-NEXT: entry: ; FNATTR-NEXT: invoke void @use2nonnull(i8* %a, i8* %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 } + attributes #0 = { "null-pointer-is-valid"="true" } +attributes #1 = { nounwind willreturn} diff --git a/test/Transforms/FunctionAttrs/norecurse.ll b/test/Transforms/FunctionAttrs/norecurse.ll index 572cd22119a..1b22d4858e0 100644 --- a/test/Transforms/FunctionAttrs/norecurse.ll +++ b/test/Transforms/FunctionAttrs/norecurse.ll @@ -130,8 +130,15 @@ define linkonce_odr i32 @leaf_redefinable() { ; Call through a function pointer ; ATTRIBUTOR-NOT: Function Attrs -; ATTRIBUTOR: define i32 @eval_func(i32 (i32)* nocapture %0, i32 %1) -define i32 @eval_func(i32 (i32)* , i32) local_unnamed_addr { +; ATTRIBUTOR: define i32 @eval_func1(i32 (i32)* nocapture nonnull %0, i32 %1) +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 ret i32 %3 } diff --git a/test/Transforms/FunctionAttrs/nosync.ll b/test/Transforms/FunctionAttrs/nosync.ll index 353835a9006..381c5abb261 100644 --- a/test/Transforms/FunctionAttrs/nosync.ll +++ b/test/Transforms/FunctionAttrs/nosync.ll @@ -45,7 +45,7 @@ entry: ; FNATTR: Function Attrs: nofree norecurse nounwind uwtable ; FNATTR-NEXT: define i32 @load_monotonic(i32* nocapture readonly %0) ; 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 { %2 = load atomic i32, i32* %0 monotonic, align 4 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-NEXT: define void @store_monotonic(i32* nocapture %0) ; 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 { store atomic i32 10, i32* %0 monotonic, align 4 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) ; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable ; 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 { %2 = load atomic i32, i32* %0 acquire, align 4 ret i32 %2 @@ -224,7 +224,8 @@ define void @scc2(i32* %0) noinline nounwind uwtable { ; FNATTR: Function Attrs: nofree norecurse nounwind ; FNATTR-NEXT: define void @foo1(i32* nocapture %0, %"struct.std::atomic"* nocapture %1) ; 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) { store i32 100, i32* %0, align 4 fence release @@ -255,8 +256,9 @@ define void @bar(i32* %0, %"struct.std::atomic"* %1) { ; TEST 13 - Fence syncscope("singlethread") seq_cst ; FNATTR: Function Attrs: nofree norecurse nounwind ; FNATTR-NEXT: define void @foo1_singlethread(i32* nocapture %0, %"struct.std::atomic"* nocapture %1) -; ATTRIBUTOR: Function Attrs: nofree nosync -; ATTRIBUTOR: define void @foo1_singlethread(i32* nocapture writeonly %0, %"struct.std::atomic"* nocapture writeonly %1) +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind willreturn +; 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) { store i32 100, i32* %0, align 4 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-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) 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 diff --git a/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll b/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll index 4a68627c574..a3ac9ffa667 100644 --- a/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll +++ b/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll @@ -70,7 +70,7 @@ return: ; preds = %if.end, %if.then } ; 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) { entry: %0 = load i32, i32* %r0, align 4 @@ -121,7 +121,7 @@ return: ; preds = %if.end, %if.then } ; 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) { entry: %0 = load i32, i32* %r0, align 4 diff --git a/test/Transforms/FunctionAttrs/readattrs.ll b/test/Transforms/FunctionAttrs/readattrs.ll index a97f49901b2..76075684bff 100644 --- a/test/Transforms/FunctionAttrs/readattrs.ll +++ b/test/Transforms/FunctionAttrs/readattrs.ll @@ -39,7 +39,7 @@ define void @test4_2(i8* %p) { } ; 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! define void @test5(i8** %p, i8* %q) { store i8* %q, i8** %p @@ -48,7 +48,7 @@ define void @test5(i8** %p, i8* %q) { declare void @test6_1() ; 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. define void @test6_2(i8** %p, i8* %q) { store i8* %q, i8** %p diff --git a/test/Transforms/InferFunctionAttrs/dereferenceable.ll b/test/Transforms/InferFunctionAttrs/dereferenceable.ll index f6580826bd1..17c76f11d6c 100644 --- a/test/Transforms/InferFunctionAttrs/dereferenceable.ll +++ b/test/Transforms/InferFunctionAttrs/dereferenceable.ll @@ -1,10 +1,17 @@ ; 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: ; https://bugs.llvm.org/show_bug.cgi?id=21780 define <4 x double> @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. %arrayidx1 = getelementptr inbounds double, double* %ptr, i64 1 %arrayidx2 = getelementptr inbounds double, double* %ptr, i64 2 @@ -23,6 +30,43 @@ define <4 x double> @PR21780(double* %ptr) { 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. define void @gep0(i8* %unused, i8* %other, i8* %ptr) { -- 2.40.0