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; @@ -718,6 +728,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,46 @@ break; } } +// 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 function that returns AbstractState with worst state. +template static State getWorst() { + State Ret; + Ret.indicatePessimisticFixpoint(); + return Ret; +} /// 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 @@ -594,6 +634,65 @@ } }; +/// Helper class for generic deduction: call site argument & backward +/// propagation +/// -> argument position. +template +struct AAArgumentFromCallSiteArgumentsAndMustBeContext + : public AAArgumentFromCallSiteArguments { + AAArgumentFromCallSiteArgumentsAndMustBeContext(const IRPosition &IRP) + : AAArgumentFromCallSiteArguments(IRP) {} + + /// Change state based on the fact that the instruction must be executed. + virtual bool knownFromInstruction(const Instruction &, StateType &, + Attributor &) = 0; + + /// Collect interesting Abstract State from Instruction in the context. + virtual void addStateFromInstruction(const Instruction &I, Attributor &) = 0; + + /// Helper function to insert interesting state. + void addState(const StateType &State) { + InterestingAbstractState.insert(&State); + } + + /// See AbstractAttribute::initialize(...). + void initialize(Attributor &A) override { + AAArgumentFromCallSiteArguments::initialize(A); + auto &S = this->getState(); + const Function *AssociatedFunction = + this->getIRPosition().getAssociatedFunction(); + + if (!AssociatedFunction) { + S.indicatePessimisticFixpoint(); + return; + } + + auto Pred = [&](const Instruction &I, StateType &State) { + addStateFromInstruction(I, A); + return knownFromInstruction(I, State, A); + }; + + A.accumulatePredOnMustBeExecutedContext( + Pred, AssociatedFunction->getEntryBlock().front(), S); + } + + /// See AbstractAttribute::updateImpl(...). + ChangeStatus updateImpl(Attributor &A) override { + auto BeforeState = this->getState(); + auto &S = this->getState(); + for (const auto *State : InterestingAbstractState) + S |= *State; + + AAArgumentFromCallSiteArguments::updateImpl(A); + return BeforeState == S ? ChangeStatus::UNCHANGED : ChangeStatus::CHANGED; + } + +private: + /// StateType instances related to deduction in the same context. + DenseSet InterestingAbstractState; +}; + /// Helper class for generic replication: function returned -> cs returned. template struct AACallSiteReturnedFromReturned : public Base { @@ -1288,8 +1387,8 @@ const auto &AA = A.getAAFor(*this, IRPosition::value(V)); if (!Stripped && this == &AA) { if (!isKnownNonZero(&V, DL, 0, /* TODO: AC */ nullptr, - /* TODO: CtxI */ nullptr, - /* TODO: DT */ nullptr)) + /* TODO: CtxI */ nullptr, + /* TODO: DT */ nullptr)) T.indicatePessimisticFixpoint(); } else { // Use abstract attribute information. @@ -1324,10 +1423,35 @@ /// NonNull attribute for function argument. struct AANonNullArgument final - : AAArgumentFromCallSiteArguments { + : AAArgumentFromCallSiteArgumentsAndMustBeContext { AANonNullArgument(const IRPosition &IRP) - : AAArgumentFromCallSiteArguments(IRP) {} + : AAArgumentFromCallSiteArgumentsAndMustBeContext< + AANonNull, AANonNullImpl, IntegerState>(IRP) {} + + /// See + /// AAArgumentFromCallSiteArgumentsAndMustBeContext::addStateFromInstruction. + void addStateFromInstruction(const Instruction &I, Attributor &A) override { + if (ImmutableCallSite ICS = ImmutableCallSite(&I)) + for (unsigned int i = 0; i < ICS.getNumArgOperands(); i++) + if (ICS.getArgOperand(i) == &getAnchorValue()) + addState(cast(A.getAAFor( + *this, IRPosition::callsite_argument(ICS, i)))); + } + + /// See AAArgumentFromCallSiteArgumentsAndMustBeContext::knownFromInstruction. + bool knownFromInstruction(const Instruction &I, IntegerState &S, + Attributor &A) override { + const DataLayout DL = A.getDataLayout(); + int64_t Offset = 0; + if (const Value *Base = getBasePointerOfPointerOperand(&I, Offset, DL)) + if (Base == &getAnchorValue()) { + S.indicateOptimisticFixpoint(); + return false; + } + return true; + } /// See AbstractAttribute::trackStatistics() void trackStatistics() const override { STATS_DECLTRACK_ARG_ATTR(nonnull) } }; @@ -1742,8 +1866,9 @@ /// See AbstractAttribute::trackStatistics() void trackStatistics() const override { - STATS_DECL(DeadInternalFunction, Function, - "Number of internal functions classified as dead (no live callsite)"); + STATS_DECL( + DeadInternalFunction, Function, + "Number of internal functions classified as dead (no live callsite)"); BUILD_STAT_NAME(DeadInternalFunction, Function) += (getAssociatedFunction()->hasInternalLinkage() && AssumedLiveBlocks.empty()) @@ -2033,6 +2158,13 @@ std::to_string(getKnownDereferenceableBytes()) + "-" + std::to_string(getAssumedDereferenceableBytes()) + ">"; } + void trackStatistics() const override { + llvm_unreachable( + "DereferenceableImpl::trackStatistics should be overrided"); + }; + ChangeStatus updateImpl(Attributor &A) override { + llvm_unreachable("DereferenceableImpl::updateImpl should be overrided"); + }; private: const AANonNull *NonNullAA = nullptr; @@ -2111,15 +2243,41 @@ /// Dereferenceable attribute for an argument struct AADereferenceableArgument final - : AAArgumentFromCallSiteArguments { + : AAArgumentFromCallSiteArgumentsAndMustBeContext< + AADereferenceable, AADereferenceableImpl, DerefState> { AADereferenceableArgument(const IRPosition &IRP) - : AAArgumentFromCallSiteArguments( - IRP) {} + : AAArgumentFromCallSiteArgumentsAndMustBeContext< + AADereferenceable, AADereferenceableImpl, DerefState>(IRP) {} + + /// See + /// AAArgumentFromCallSiteArgumentsAndMustBeContext::addStateFromInstruction. + void addStateFromInstruction(const Instruction &I, Attributor &A) override { + if (ImmutableCallSite ICS = ImmutableCallSite(&I)) + for (unsigned int i = 0; i < ICS.getNumArgOperands(); i++) + if (ICS.getArgOperand(i) == &getAnchorValue()) + addState(cast(A.getAAFor( + *this, IRPosition::callsite_argument(ICS, i)))); + } + + /// See AAArgumentFromCallSiteArgumentsAndMustBeContext::knownFromInstruction. + bool knownFromInstruction(const Instruction &I, DerefState &S, + Attributor &A) override { + const DataLayout DL = A.getDataLayout(); + int64_t Offset = 0; + if (const Value *Base = getBasePointerOfPointerOperand(&I, Offset, DL)) + if (Base == &getAnchorValue()) { + uint32_t DerefBytes = + Offset + + DL.getTypeStoreSize( + getPointerOperand(&I)->getType()->getPointerElementType()); + + S.takeKnownDerefBytesMaximum(DerefBytes); + } + return true; + } /// See AbstractAttribute::trackStatistics() - void trackStatistics() const override{ + void trackStatistics() const override { STATS_DECLTRACK_ARG_ATTR(dereferenceable) } }; 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 @@ -50,3 +50,17 @@ ret i32* %0 } +; TEST 5 +; Deduction from memory inst. + +define void @test5(i32* %a, i32* %b, i8* %c) local_unnamed_addr { +; ATTRIBUTOR: define void @test5(i32* nonnull dereferenceable(4) %a, i32* nonnull dereferenceable(1) %b, i8* nonnull dereferenceable(4) %c) + %bp = bitcast i32* %b to i8* + %cp = bitcast i8* %c to i32* + + %d = load i32, i32* %a + %e = load i8, i8* %bp + %f = load i32, i32* %cp + 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