Index: llvm/include/llvm/Analysis/ValueTracking.h =================================================================== --- llvm/include/llvm/Analysis/ValueTracking.h +++ llvm/include/llvm/Analysis/ValueTracking.h @@ -242,21 +242,21 @@ /// This is a wrapper around Value::stripAndAccumulateConstantOffsets that /// creates and later unpacks the required APInt. inline Value *GetPointerBaseWithConstantOffset(Value *Ptr, int64_t &Offset, - const DataLayout &DL) { + const DataLayout &DL, + bool AllowNonInbounds = true) { APInt OffsetAPInt(DL.getIndexTypeSizeInBits(Ptr->getType()), 0); Value *Base = - Ptr->stripAndAccumulateConstantOffsets(DL, OffsetAPInt, - /* AllowNonInbounds */ true); + Ptr->stripAndAccumulateConstantOffsets(DL, OffsetAPInt, AllowNonInbounds); Offset = OffsetAPInt.getSExtValue(); return Base; } - inline const Value *GetPointerBaseWithConstantOffset(const Value *Ptr, - int64_t &Offset, - const DataLayout &DL) { - return GetPointerBaseWithConstantOffset(const_cast(Ptr), Offset, - DL); + inline const Value * + GetPointerBaseWithConstantOffset(const Value *Ptr, int64_t &Offset, + const DataLayout &DL, + bool AllowNonInbounds = true) { + return GetPointerBaseWithConstantOffset(const_cast(Ptr), Offset, DL, + AllowNonInbounds); } - /// Returns true if the GEP is based on a pointer to a string (array of // \p CharSize integers) and is indexing into this string. bool isGEPBasedOnPointerToString(const GEPOperator *GEP, Index: llvm/include/llvm/Transforms/IPO/Attributor.h =================================================================== --- llvm/include/llvm/Transforms/IPO/Attributor.h +++ llvm/include/llvm/Transforms/IPO/Attributor.h @@ -97,6 +97,7 @@ #define LLVM_TRANSFORMS_IPO_ATTRIBUTOR_H #include "llvm/ADT/SetVector.h" +#include "llvm/Analysis/MustExecute.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/PassManager.h" @@ -498,7 +499,8 @@ /// reusable, it is advised to inherit from the InformationCache and cast the /// instance down in the abstract attributes. struct InformationCache { - InformationCache(const DataLayout &DL) : DL(DL) {} + InformationCache(const DataLayout &DL) + : DL(DL), Explorer(/* ExploreInterBlock */ true) {} /// A map type from opcodes to instructions with this opcode. using OpcodeInstMapTy = DenseMap>; @@ -517,6 +519,11 @@ return FuncRWInstsMap[&F]; } + /// Return MustBeExecutedContextExplorer + MustBeExecutedContextExplorer &getMustBeExecutedContextExplorer() { + return Explorer; + } + private: /// A map type from functions to opcode to instruction maps. using FuncInstOpcodeMapTy = DenseMap; @@ -534,6 +541,9 @@ /// The datalayout used in the module. const DataLayout &DL; + /// MustBeExecutedContextExplorer + MustBeExecutedContextExplorer Explorer; + /// Give the Attributor access to the members so /// Attributor::identifyDefaultAbstractAttributes(...) can initialize them. friend struct Attributor; @@ -719,6 +729,26 @@ /// Return the data layout associated with the anchor scope. const DataLayout &getDataLayout() const { return InfoCache.DL; } + /// Accumulate \p Pred on all instructions in the must-be-executed-context. + /// + /// This method will evaluate \p Pred on all instructions in the same context + /// as \p PP. + template + bool accumulatePredOnMustBeExecutedContext( + const llvm::function_ref &Pred, + const Instruction &PP, StateType &State) { + + MustBeExecutedContextExplorer &Explorer = + getInfoCache().getMustBeExecutedContextExplorer(); + + // Traverse must-be-excuted-context. + for (const Instruction *I : Explorer.range(&PP)) + if (!Pred(*I, State)) + return false; + + return true; + } + private: /// The set of all abstract attributes. ///{ Index: llvm/lib/Transforms/IPO/Attributor.cpp =================================================================== --- llvm/lib/Transforms/IPO/Attributor.cpp +++ llvm/lib/Transforms/IPO/Attributor.cpp @@ -454,6 +454,40 @@ } } +// Helper function that returns pointer operand of memory instruction. +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; +} + +// Helper function that returns base pointer of memory instruction operand. +// Only inbounds GEPs are traced. +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); +} + /// Helper functions to clamp a state \p S of type \p StateType with the /// information in \p R and indicate/return if \p S did change (as-in update is /// required to be run again). @@ -608,7 +642,8 @@ }; /// Helper class for generic replication: function returned -> cs returned. -template +template struct AACallSiteReturnedFromReturned : public Base { AACallSiteReturnedFromReturned(const IRPosition &IRP) : Base(IRP) {} @@ -632,6 +667,83 @@ } }; +/// Helper class for generic deduction using must-be-executed-context +template +struct AAFromMustBeExecutedContext : public Base { + AAFromMustBeExecutedContext(const IRPosition &IRP) : Base(IRP) {} + + /// Collect interesting State from instruction in the context. + virtual void addStateFromInstruction(const Instruction &I, Attributor &A) { + if (ImmutableCallSite ICS = ImmutableCallSite(&I)) + for (unsigned int i = 0; i < ICS.getNumArgOperands(); i++) + if (ICS.getArgOperand(i) == + &this->getIRPosition().getAssociatedValue()) { + + auto &AA = + A.getAAFor(*this, IRPosition::callsite_argument(ICS, i)); + LLVM_DEBUG(dbgs() << "[Attributor][MCtx] " + << this->getIRPosition().getAnchorValue() << " " + << I << " AA:" << AA.getAsStr() << " " << &AA << " " + << AA.getIRPosition().getAnchorValue() << "\n"); + addState(static_cast(AA)); + } + } + + /// Helper function to insert interesting state. + void addState(const StateType &State) { + InterestingAbstractState.insert(&State); + } + + /// See AbstractAttribute::updateImpl(...). + ChangeStatus updateImpl(Attributor &A) override { + auto BeforeState = this->getState(); + auto &S = this->getState(); + + if (!DoneFirstIteration) { + auto &IRP = this->getIRPosition(); + Instruction *CtxI = IRP.getCtxI(); + + if (!CtxI) + return ChangeStatus::UNCHANGED; + + auto Pred = [&](const Instruction &I, StateType &State) { + addStateFromInstruction(I, A); + return Base::knownFromInstruction(I, State, A); + }; + + A.accumulatePredOnMustBeExecutedContext(Pred, *CtxI, S); + DoneFirstIteration = true; + } + + for (const auto *State : InterestingAbstractState) + S += *State; + + return BeforeState == S ? ChangeStatus::UNCHANGED : ChangeStatus::CHANGED; + } + +private: + /// StateType instances related to deduction in the same context. + DenseSet InterestingAbstractState; + + /// Flag for first iteration + bool DoneFirstIteration = false; +}; + +template +using AAArgumentFromCallSiteArgumentsAndMustBeExecutedContext = + AAComposeTwoGenericDeduction; + +template +using AACallSiteReturnedFromReturnedAndMustBeExecutedContext = + AAComposeTwoGenericDeduction; + /// -----------------------NoUnwind Function Attribute-------------------------- struct AANoUnwindImpl : AANoUnwind { @@ -1300,6 +1412,18 @@ indicateOptimisticFixpoint(); } + bool knownFromInstruction(const Instruction &I, IntegerState &S, + Attributor &A) { + const DataLayout DL = A.getDataLayout(); + int64_t Offset = 0; + if (const Value *Base = getBasePointerOfPointerOperand(&I, Offset, DL)) + if (Base == &this->getIRPosition().getAssociatedValue()) { + S.indicateOptimisticFixpoint(); + return false; + } + return true; + } + /// See AbstractAttribute::getAsStr(). const std::string getAsStr() const override { return getAssumed() ? "nonnull" : "may-null"; @@ -1307,12 +1431,14 @@ }; /// 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; @@ -1330,6 +1456,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, @@ -1373,9 +1501,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) } @@ -1390,9 +1521,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) } @@ -1970,6 +2104,22 @@ const StateType &getState() const override { return *this; } /// } + bool knownFromInstruction(const Instruction &I, DerefState &S, + Attributor &A) { + const DataLayout DL = A.getDataLayout(); + int64_t Offset = 0; + if (const Value *Base = getBasePointerOfPointerOperand(&I, Offset, DL)) + if (Base == &this->getIRPosition().getAssociatedValue()) { + uint32_t DerefBytes = + Offset + + DL.getTypeStoreSize( + getPointerOperand(&I)->getType()->getPointerElementType()); + + S.takeKnownDerefBytesMaximum(DerefBytes); + } + return true; + } + void getDeducedAttributes(LLVMContext &Ctx, SmallVectorImpl &Attrs) const override { // TODO: Add *_globally support @@ -1994,12 +2144,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 { @@ -2058,7 +2212,7 @@ A, getIRPosition(), *this, T, VisitValueCB)) return indicatePessimisticFixpoint(); - return clampStateAndIndicateChange(getState(), T); + return Change | clampStateAndIndicateChange(getState(), T); } /// See AbstractAttribute::trackStatistics() @@ -2083,15 +2237,14 @@ /// Dereferenceable attribute for an argument struct AADereferenceableArgument final - : AAArgumentFromCallSiteArguments { + : AAArgumentFromCallSiteArgumentsAndMustBeExecutedContext< + AADereferenceable, AADereferenceableImpl, DerefState> { AADereferenceableArgument(const IRPosition &IRP) - : AAArgumentFromCallSiteArguments( - IRP) {} + : AAArgumentFromCallSiteArgumentsAndMustBeExecutedContext< + AADereferenceable, AADereferenceableImpl, DerefState>(IRP) {} /// See AbstractAttribute::trackStatistics() - void trackStatistics() const override{ + void trackStatistics() const override { STATS_DECLTRACK_ARG_ATTR(dereferenceable) } }; @@ -2108,7 +2261,16 @@ }; /// Dereferenceable attribute deduction for a call site return value. -using AADereferenceableCallSiteReturned = AADereferenceableReturned; +struct AADereferenceableCallSiteReturned final + : AACallSiteReturnedFromReturnedAndMustBeExecutedContext< + AADereferenceable, AADereferenceableImpl> { + AADereferenceableCallSiteReturned(const IRPosition &IRP) + : AACallSiteReturnedFromReturnedAndMustBeExecutedContext< + AADereferenceable, AADereferenceableImpl>(IRP) {} + + /// See AbstractAttribute::trackStatistics() + void trackStatistics() const override { STATS_DECLTRACK_CSRET_ATTR(nonnull) } +}; // ------------------------ Align Argument Attribute ------------------------ Index: llvm/test/Transforms/FunctionAttrs/align.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/align.ll +++ llvm/test/Transforms/FunctionAttrs/align.ll @@ -173,7 +173,7 @@ ; FIXME: This will work with an upcoming patch (D66618 or similar) ; define align 32 i32* @test10a(i32* align 32 %p) -; ATTRIBUTOR: define i32* @test10a(i32* align 32 %p) +; ATTRIBUTOR: define i32* @test10a(i32* nonnull align 32 dereferenceable(4) %p) define i32* @test10a(i32* align 32 %p) { ; ATTRIBUTOR: %l = load i32, i32* %p, align 32 %l = load i32, i32* %p @@ -201,7 +201,7 @@ ; FIXME: This will work with an upcoming patch (D66618 or similar) ; define align 32 i32* @test10b(i32* align 32 %p) -; ATTRIBUTOR: define i32* @test10b(i32* align 32 %p) +; ATTRIBUTOR: define i32* @test10b(i32* nonnull align 32 dereferenceable(4) %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 @@ -248,7 +248,8 @@ ; } ; ; There should *not* be a no-capture attribute on %a -; CHECK: define i64* @not_captured_but_returned_0(i64* returned %a) +; CHECK: define nonnull dereferenceable(8) i64* @not_captured_but_returned_0(i64* nonnull returned dereferenceable(8) %a) + define i64* @not_captured_but_returned_0(i64* %a) #0 { entry: store i64 0, i64* %a, align 8 @@ -263,7 +264,7 @@ ; } ; ; There should *not* be a no-capture attribute on %a -; CHECK: define nonnull i64* @not_captured_but_returned_1(i64* %a) +; CHECK: define nonnull dereferenceable(8) i64* @not_captured_but_returned_1(i64* nonnull dereferenceable(16) %a) define i64* @not_captured_but_returned_1(i64* %a) #0 { entry: %add.ptr = getelementptr inbounds i64, i64* %a, i64 1 @@ -324,7 +325,7 @@ ; } ; ; There should *not* be a no-capture attribute on %a -; CHECK: define nonnull i64* @negative_test_not_captured_but_returned_call_1a(i64* %a) +; CHECK: define nonnull dereferenceable(8) i64* @negative_test_not_captured_but_returned_call_1a(i64* %a) define i64* @negative_test_not_captured_but_returned_call_1a(i64* %a) #0 { entry: %call = call i64* @not_captured_but_returned_1(i64* %a) @@ -359,6 +360,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* readonly %a) +; BOTH-NEXT: define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @rt0(i32* 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-disable=false -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR +; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations=16 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR declare void @deref_phi_user(i32* %a); @@ -111,3 +111,79 @@ 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 +} 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* nonnull %0) +; CHECK-NEXT: define internal i32 @internal_load(i32* 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/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]] @@ -401,8 +478,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* %bogus1, i8* %b) +; ATTRIBUTOR-LABEL: @parent8(i8* nonnull %a, i8* %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 +505,7 @@ unreachable } + ; BOTH: define nonnull i32* @gep1( define i32* @gep1(i32* %p) { %q = getelementptr inbounds i32, i32* %p, i32 1 @@ -459,3 +536,4 @@ } attributes #0 = { "null-pointer-is-valid"="true" } +attributes #1 = { nounwind willreturn} 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* %0, %"struct.std::atomic"* %1) +; ATTRIBUTOR: define void @foo1(i32* nonnull dereferenceable(4) %0, %"struct.std::atomic"* nonnull dereferenceable(1) %1) define void @foo1(i32* %0, %"struct.std::atomic"* %1) { store i32 100, i32* %0, align 4 fence release @@ -236,7 +236,7 @@ ; FNATTR: Function Attrs: nofree norecurse nounwind ; FNATTR-NEXT: define void @bar(i32* nocapture readnone %0, %"struct.std::atomic"* nocapture readonly %1) ; ATTRIBUTOR-NOT: nosync -; ATTRIBUTOR: define void @bar(i32* %0, %"struct.std::atomic"* %1) +; ATTRIBUTOR: define void @bar(i32* %0, %"struct.std::atomic"* nonnull dereferenceable(1) %1) define void @bar(i32* %0, %"struct.std::atomic"* %1) { %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0 br label %4 @@ -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* %0, %"struct.std::atomic"* %1) +; ATTRIBUTOR: define void @foo1_singlethread(i32* nonnull dereferenceable(4) %0, %"struct.std::atomic"* nonnull dereferenceable(1) %1) define void @foo1_singlethread(i32* %0, %"struct.std::atomic"* %1) { store i32 100, i32* %0, align 4 fence syncscope("singlethread") release @@ -268,7 +268,7 @@ ; 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: define void @bar_singlethread(i32* %0, %"struct.std::atomic"* %1) +; ATTRIBUTOR: define void @bar_singlethread(i32* %0, %"struct.std::atomic"* nonnull dereferenceable(1) %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 br label %4 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) {