Index: llvm/include/llvm/Transforms/IPO/Attributor.h =================================================================== --- llvm/include/llvm/Transforms/IPO/Attributor.h +++ llvm/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" @@ -561,7 +562,7 @@ /// 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) @@ -592,6 +593,11 @@ return FuncRWInstsMap[&F]; } + /// Return MustBeExecutedContextExplorer + MustBeExecutedContextExplorer &getMustBeExecutedContextExplorer() { + return Explorer; + } + /// Return TargetLibraryInfo for function \p F. TargetLibraryInfo *getTargetLibraryInfoForFunction(const Function &F) { return AG.getAnalysis(F); @@ -629,6 +635,9 @@ /// The datalayout used in the module. const DataLayout &DL; + /// MustBeExecutedContextExplorer + MustBeExecutedContextExplorer Explorer; + /// Getters for analysis. AnalysisGetter &AG; @@ -1666,6 +1675,11 @@ 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(); } Index: llvm/lib/Transforms/IPO/Attributor.cpp =================================================================== --- llvm/lib/Transforms/IPO/Attributor.cpp +++ llvm/lib/Transforms/IPO/Attributor.cpp @@ -288,6 +288,35 @@ 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 *getBasePointerOfPointerOperand(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; @@ -642,7 +671,8 @@ }; /// Helper class for generic replication: function returned -> cs returned. -template +template struct AACallSiteReturnedFromReturned : public Base { AACallSiteReturnedFromReturned(const IRPosition &IRP) : Base(IRP) {} @@ -666,6 +696,72 @@ } }; +/// 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(); + + // FIXME: These calculation might be too costly. + for (const Use *U : Uses) + if (const Instruction *UserI = dyn_cast(U->getUser())) + if (std::find(Explorer.begin(CtxI), Explorer.end(), UserI) != + Explorer.end()) + if (Base::followUse(A, U, UserI)) + for (const Use &Us : UserI->uses()) + Uses.insert(&Us); + 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 { @@ -1422,6 +1518,44 @@ }; /// ------------------------ 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; + if (ImmutableCallSite ICS = ImmutableCallSite(I)) { + if (ICS.isBundleOperand(U)) + return 0; + + if (ICS.isCallee(U)) { + IsNonNull = true; + 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; + const DataLayout &DL = A.getInfoCache().getDL(); + if (const Value *Base = getBasePointerOfPointerOperand(I, Offset, DL)) { + if (Base == &AssociatedValue) { + uint32_t DerefBytes = + Offset + + DL.getTypeStoreSize( + getPointerOperand(I)->getType()->getPointerElementType()); + + IsNonNull |= DerefBytes > 0; + return DerefBytes; + } + } + return 0; +} struct AANonNullImpl : AANonNull { AANonNullImpl(const IRPosition &IRP) : AANonNull(IRP) {} @@ -1433,19 +1567,34 @@ AANonNull::initialize(A); } + /// 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); + takeKnownMaximum(IsNonNull); + return TrackUse; + } + /// See AbstractAttribute::getAsStr(). const std::string getAsStr() const override { - return getAssumed() ? "nonnull" : "may-null"; + + return std::string("") + (getAssumed() ? "nonnull" : "may-null") + + (std::to_string(getAssumed()) + "/" + std::to_string(getKnown())); + // return getAssumed() ? "nonnull" : "may-null"; } }; /// 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; @@ -1463,6 +1612,8 @@ /// See AbstractAttribute::updateImpl(...). ChangeStatus updateImpl(Attributor &A) override { + ChangeStatus Change = Base::updateImpl(A); + const DataLayout &DL = A.getDataLayout(); auto VisitValueCB = [&](Value &V, AAAlign::StateType &T, @@ -1506,9 +1657,12 @@ /// 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) } @@ -1523,9 +1677,12 @@ /// 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) } @@ -2278,6 +2435,16 @@ 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 @@ -2302,12 +2469,16 @@ }; /// 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 { @@ -2366,7 +2537,7 @@ A, getIRPosition(), *this, T, VisitValueCB)) return indicatePessimisticFixpoint(); - return clampStateAndIndicateChange(getState(), T); + return Change | clampStateAndIndicateChange(getState(), T); } /// See AbstractAttribute::trackStatistics() @@ -2391,12 +2562,11 @@ /// 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 { @@ -2416,13 +2586,16 @@ }; /// 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(); @@ -2434,11 +2607,14 @@ // 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() Index: llvm/test/Transforms/FunctionAttrs/align.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/align.ll +++ llvm/test/Transforms/FunctionAttrs/align.ll @@ -175,7 +175,7 @@ ; 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 @@ ; 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 Index: llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll +++ llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll @@ -245,7 +245,7 @@ ; } ; ; There should *not* be a no-capture attribute on %a -; CHECK: define i64* @not_captured_but_returned_0(i64* returned "no-capture-maybe-returned" %a) +; CHECK: define nonnull dereferenceable(8) i64* @not_captured_but_returned_0(i64* nonnull returned 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 @@ -356,6 +356,7 @@ ; ; 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 { Index: llvm/test/Transforms/FunctionAttrs/arg_returned.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/arg_returned.ll +++ llvm/test/Transforms/FunctionAttrs/arg_returned.ll @@ -261,7 +261,7 @@ ; ; 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 @@ -279,7 +279,7 @@ ; ; 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 Index: llvm/test/Transforms/FunctionAttrs/dereferenceable.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/dereferenceable.ll +++ llvm/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,87 @@ 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() { +; FIXME: This should be `define nonnull dereferenceable(4) 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 +} Index: llvm/test/Transforms/FunctionAttrs/internal-noalias.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/internal-noalias.ll +++ llvm/test/Transforms/FunctionAttrs/internal-noalias.ll @@ -10,8 +10,7 @@ ; FIXME: Should be something like this. ; define internal i32 @noalias_args(i32* nocapture readonly %A, i32* noalias nocapture readonly %B) -; CHECK: define internal i32 @noalias_args(i32* nocapture %A, i32* noalias nocapture %B) - +; CHECK: define internal i32 @noalias_args(i32* nocapture nonnull dereferenceable(4) %A, i32* noalias nocapture nonnull dereferenceable(4) %B) define internal i32 @noalias_args(i32* %A, i32* %B) #0 { entry: %0 = load i32, i32* %A, align 4 @@ -25,7 +24,7 @@ ; 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 %A, i32* nocapture %B) +; CHECK: define internal i32 @noalias_args_argmem(i32* nocapture nonnull dereferenceable(4) %A, i32* nocapture nonnull dereferenceable(4) %B) ; define internal i32 @noalias_args_argmem(i32* %A, i32* %B) #1 { entry: Index: llvm/test/Transforms/FunctionAttrs/liveness.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/liveness.ll +++ llvm/test/Transforms/FunctionAttrs/liveness.ll @@ -40,7 +40,7 @@ } ; CHECK: Function Attrs: nofree norecurse nosync nounwind uwtable willreturn -; CHECK-NEXT: define internal i32 @internal_load(i32* nocapture nonnull %0) +; CHECK-NEXT: define internal i32 @internal_load(i32* nocapture nonnull dereferenceable(4) %0) define internal i32 @internal_load(i32*) norecurse nounwind uwtable { %2 = load i32, i32* %0, align 4 ret i32 %2 Index: llvm/test/Transforms/FunctionAttrs/noalias_returned.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/noalias_returned.ll +++ llvm/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. Index: llvm/test/Transforms/FunctionAttrs/nocapture.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/nocapture.ll +++ llvm/test/Transforms/FunctionAttrs/nocapture.ll @@ -119,7 +119,9 @@ 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 @@ 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) @@ -215,19 +218,22 @@ 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 Index: llvm/test/Transforms/FunctionAttrs/nonnull.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/nonnull.ll +++ llvm/test/Transforms/FunctionAttrs/nonnull.ll @@ -236,6 +236,104 @@ 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ 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 @@ ; 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]] @@ -402,7 +479,7 @@ 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 %bogus1, i8* %b) +; ATTRIBUTOR-LABEL: @parent8(i8* nonnull %a, i8* nocapture %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) @@ -429,6 +506,7 @@ unreachable } + ; BOTH: define nonnull i32* @gep1( define i32* @gep1(i32* %p) { %q = getelementptr inbounds i32, i32* %p, i32 1 @@ -471,3 +549,4 @@ } attributes #0 = { "null-pointer-is-valid"="true" } +attributes #1 = { nounwind willreturn} Index: llvm/test/Transforms/FunctionAttrs/norecurse.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/norecurse.ll +++ llvm/test/Transforms/FunctionAttrs/norecurse.ll @@ -130,7 +130,7 @@ ; Call through a function pointer ; ATTRIBUTOR-NOT: Function Attrs -; ATTRIBUTOR: define i32 @eval_func(i32 (i32)* nocapture %0, i32 %1) +; ATTRIBUTOR: define i32 @eval_func(i32 (i32)* nocapture nonnull %0, i32 %1) define i32 @eval_func(i32 (i32)* , i32) local_unnamed_addr { %3 = tail call i32 %0(i32 %1) #2 ret i32 %3 Index: llvm/test/Transforms/FunctionAttrs/nosync.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/nosync.ll +++ llvm/test/Transforms/FunctionAttrs/nosync.ll @@ -45,7 +45,7 @@ ; 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 @@ ; 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 %0) +; ATTRIBUTOR-NEXT: define void @store_monotonic(i32* nocapture nonnull 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 @@ ; 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,7 @@ ; 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 %0, %"struct.std::atomic"* nocapture %1) +; ATTRIBUTOR: define void @foo1(i32* nocapture nonnull dereferenceable(4) %0, %"struct.std::atomic"* nocapture %1) define void @foo1(i32* %0, %"struct.std::atomic"* %1) { store i32 100, i32* %0, align 4 fence release @@ -256,7 +256,7 @@ ; 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 %0, %"struct.std::atomic"* nocapture %1) +; ATTRIBUTOR: define void @foo1_singlethread(i32* nocapture nonnull dereferenceable(4) %0, %"struct.std::atomic"* nocapture %1) define void @foo1_singlethread(i32* %0, %"struct.std::atomic"* %1) { store i32 100, i32* %0, align 4 fence syncscope("singlethread") release Index: llvm/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll +++ llvm/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll @@ -70,7 +70,7 @@ } ; 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 @@ } ; 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 Index: llvm/test/Transforms/InferFunctionAttrs/dereferenceable.ll =================================================================== --- llvm/test/Transforms/InferFunctionAttrs/dereferenceable.ll +++ llvm/test/Transforms/InferFunctionAttrs/dereferenceable.ll @@ -1,10 +1,14 @@ ; 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) +; ATTRIBUTOR-LABEL: @PR21780(double* nonnull dereferenceable(32) %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 +27,39 @@ ret <4 x double> %shuffle } + +define double @PR21780_only_access3_with_inbounds(double* %ptr) { +; CHECK-LABEL: @PR21780_only_access3_with_inbounds(double* %ptr) +; ATTRIBUTOR-LABEL: @PR21780_only_access3_with_inbounds(double* nonnull dereferenceable(32) %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* %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* nonnull 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) {