llvm.org GIT mirror llvm / e9ce3e9
[Attributor][MustExec] Deduce dereferenceable and nonnull attribute using MustBeExecutedContextExplorer Summary: In D65186 and related patches, MustBeExecutedContextExplorer is introduced. This enables us to traverse instructions guaranteed to execute from function entry. If we can know the argument is used as `dereferenceable` or `nonnull` in these instructions, we can mark `dereferenceable` or `nonnull` in the argument definition: 1. Memory instruction (similar to D64258) Trace memory instruction pointer operand. Currently, only inbounds GEPs are traced. ``` define i64* @f(i64* %a) { entry: %add.ptr = getelementptr inbounds i64, i64* %a, i64 1 ; (because of inbounds GEP we can know that %a is at least dereferenceable(16)) store i64 1, i64* %add.ptr, align 8 ret i64* %add.ptr ; dereferenceable 8 (because above instruction stores into it) } ``` 2. Propagation from callsite (similar to D27855) If `deref` or `nonnull` are known in call site parameter attributes we can also say that argument also that attribute. ``` declare void @use3(i8* %x, i8* %y, i8* %z); declare void @use3nonnull(i8* nonnull %x, i8* nonnull %y, i8* nonnull %z); define void @parent1(i8* %a, i8* %b, i8* %c) { call void @use3nonnull(i8* %b, i8* %c, i8* %a) ; Above instruction is always executed so we can say that@parent1(i8* nonnnull %a, i8* nonnull %b, i8* nonnull %c) call void @use3(i8* %c, i8* %a, i8* %b) ret void } ``` Reviewers: jdoerfert, sstefan1, spatel, reames Reviewed By: jdoerfert Subscribers: xbolva00, hiraditya, jfb, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D65402 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@374063 91177308-0d34-0410-b5e6-96231b3b80d8 Hideto Ueno 1 year, 1 month ago
17 changed file(s) with 520 addition(s) and 91 deletion(s). Raw diff Collapse all Expand all
100100 #include "llvm/ADT/SetVector.h"
101101 #include "llvm/Analysis/AliasAnalysis.h"
102102 #include "llvm/Analysis/CallGraph.h"
103 #include "llvm/Analysis/MustExecute.h"
103104 #include "llvm/Analysis/TargetLibraryInfo.h"
104105 #include "llvm/IR/CallSite.h"
105106 #include "llvm/IR/PassManager.h"
594595 /// instance down in the abstract attributes.
595596 struct InformationCache {
596597 InformationCache(const Module &M, AnalysisGetter &AG)
597 : DL(M.getDataLayout()), AG(AG) {
598 : DL(M.getDataLayout()), Explorer(/* ExploreInterBlock */ true), AG(AG) {
598599
599600 CallGraph *CG = AG.getAnalysis(M);
600601 if (!CG)
625626 return FuncRWInstsMap[&F];
626627 }
627628
629 /// Return MustBeExecutedContextExplorer
630 MustBeExecutedContextExplorer &getMustBeExecutedContextExplorer() {
631 return Explorer;
632 }
633
628634 /// Return TargetLibraryInfo for function \p F.
629635 TargetLibraryInfo *getTargetLibraryInfoForFunction(const Function &F) {
630636 return AG.getAnalysis(F);
661667
662668 /// The datalayout used in the module.
663669 const DataLayout &DL;
670
671 /// MustBeExecutedContextExplorer
672 MustBeExecutedContextExplorer Explorer;
664673
665674 /// Getters for analysis.
666675 AnalysisGetter &AG;
17131722 return NonNullAA && NonNullAA->isAssumedNonNull();
17141723 }
17151724
1725 /// Return true if we know that the underlying value is nonnull.
1726 bool isKnownNonNull() const {
1727 return NonNullAA && NonNullAA->isKnownNonNull();
1728 }
1729
17161730 /// Return true if we assume that underlying value is
17171731 /// dereferenceable(_or_null) globally.
17181732 bool isAssumedGlobal() const { return GlobalState.getAssumed(); }
286286 }
287287
288288 llvm_unreachable("Expected enum or string attribute!");
289 }
290 static const Value *getPointerOperand(const Instruction *I) {
291 if (auto *LI = dyn_cast(I))
292 if (!LI->isVolatile())
293 return LI->getPointerOperand();
294
295 if (auto *SI = dyn_cast(I))
296 if (!SI->isVolatile())
297 return SI->getPointerOperand();
298
299 if (auto *CXI = dyn_cast(I))
300 if (!CXI->isVolatile())
301 return CXI->getPointerOperand();
302
303 if (auto *RMWI = dyn_cast(I))
304 if (!RMWI->isVolatile())
305 return RMWI->getPointerOperand();
306
307 return nullptr;
308 }
309 static const Value *getBasePointerOfAccessPointerOperand(const Instruction *I,
310 int64_t &BytesOffset,
311 const DataLayout &DL) {
312 const Value *Ptr = getPointerOperand(I);
313 if (!Ptr)
314 return nullptr;
315
316 return GetPointerBaseWithConstantOffset(Ptr, BytesOffset, DL,
317 /*AllowNonInbounds*/ false);
289318 }
290319
291320 ChangeStatus AbstractAttribute::update(Attributor &A) {
653682 };
654683
655684 /// Helper class for generic replication: function returned -> cs returned.
656 template >
685 template ,
686 typename StateType = typename AAType::StateType>
657687 struct AACallSiteReturnedFromReturned : public Base {
658688 AACallSiteReturnedFromReturned(const IRPosition &IRP) : Base(IRP) {}
659689
676706 S, static_cast(AA.getState()));
677707 }
678708 };
709
710 /// Helper class for generic deduction using must-be-executed-context
711 /// Base class is required to have `followUse` method.
712
713 /// bool followUse(Attributor &A, const Use *U, const Instruction *I)
714 /// \param U Underlying use.
715 /// \param I The user of the \p U.
716 /// `followUse` returns true if the value should be tracked transitively.
717
718 template
719 typename StateType = typename AAType::StateType>
720 struct AAFromMustBeExecutedContext : public Base {
721 AAFromMustBeExecutedContext(const IRPosition &IRP) : Base(IRP) {}
722
723 void initialize(Attributor &A) override {
724 Base::initialize(A);
725 IRPosition &IRP = this->getIRPosition();
726 Instruction *CtxI = IRP.getCtxI();
727
728 if (!CtxI)
729 return;
730
731 for (const Use &U : IRP.getAssociatedValue().uses())
732 Uses.insert(&U);
733 }
734
735 /// See AbstractAttribute::updateImpl(...).
736 ChangeStatus updateImpl(Attributor &A) override {
737 auto BeforeState = this->getState();
738 auto &S = this->getState();
739 Instruction *CtxI = this->getIRPosition().getCtxI();
740 if (!CtxI)
741 return ChangeStatus::UNCHANGED;
742
743 MustBeExecutedContextExplorer &Explorer =
744 A.getInfoCache().getMustBeExecutedContextExplorer();
745
746 SetVector NextUses;
747
748 for (const Use *U : Uses) {
749 if (const Instruction *UserI = dyn_cast(U->getUser())) {
750 auto EIt = Explorer.begin(CtxI), EEnd = Explorer.end(CtxI);
751 bool Found = EIt.count(UserI);
752 while (!Found && ++EIt != EEnd)
753 Found = EIt.getCurrentInst() == UserI;
754 if (Found && Base::followUse(A, U, UserI))
755 for (const Use &Us : UserI->uses())
756 NextUses.insert(&Us);
757 }
758 }
759 for (const Use *U : NextUses)
760 Uses.insert(U);
761
762 return BeforeState == S ? ChangeStatus::UNCHANGED : ChangeStatus::CHANGED;
763 }
764
765 private:
766 /// Container for (transitive) uses of the associated value.
767 SetVector Uses;
768 };
769
770 template
771 typename StateType = typename AAType::StateType>
772 using AAArgumentFromCallSiteArgumentsAndMustBeExecutedContext =
773 AAComposeTwoGenericDeduction
774 AAFromMustBeExecutedContext,
775 AAArgumentFromCallSiteArguments>;
776
777 template
778 typename StateType = typename AAType::StateType>
779 using AACallSiteReturnedFromReturnedAndMustBeExecutedContext =
780 AAComposeTwoGenericDeduction
781 AAFromMustBeExecutedContext,
782 AACallSiteReturnedFromReturned>;
679783
680784 /// -----------------------NoUnwind Function Attribute--------------------------
681785
14331537 };
14341538
14351539 /// ------------------------ NonNull Argument Attribute ------------------------
1540 static int64_t getKnownNonNullAndDerefBytesForUse(
1541 Attributor &A, AbstractAttribute &QueryingAA, Value &AssociatedValue,
1542 const Use *U, const Instruction *I, bool &IsNonNull, bool &TrackUse) {
1543 // TODO: Add GEP support
1544 TrackUse = false;
1545
1546 const Function *F = I->getFunction();
1547 bool NullPointerIsDefined = F ? F->nullPointerIsDefined() : true;
1548 const DataLayout &DL = A.getInfoCache().getDL();
1549 if (ImmutableCallSite ICS = ImmutableCallSite(I)) {
1550 if (ICS.isBundleOperand(U))
1551 return 0;
1552
1553 if (ICS.isCallee(U)) {
1554 IsNonNull |= !NullPointerIsDefined;
1555 return 0;
1556 }
1557
1558 unsigned ArgNo = ICS.getArgumentNo(U);
1559 IRPosition IRP = IRPosition::callsite_argument(ICS, ArgNo);
1560 auto &DerefAA = A.getAAFor(QueryingAA, IRP);
1561 IsNonNull |= DerefAA.isKnownNonNull();
1562 return DerefAA.getKnownDereferenceableBytes();
1563 }
1564
1565 int64_t Offset;
1566 if (const Value *Base = getBasePointerOfAccessPointerOperand(I, Offset, DL)) {
1567 if (Base == &AssociatedValue) {
1568 int64_t DerefBytes =
1569 Offset +
1570 (int64_t)DL.getTypeStoreSize(
1571 getPointerOperand(I)->getType()->getPointerElementType());
1572
1573 IsNonNull |= !NullPointerIsDefined;
1574 return DerefBytes;
1575 }
1576 }
1577
1578 return 0;
1579 }
14361580 struct AANonNullImpl : AANonNull {
14371581 AANonNullImpl(const IRPosition &IRP) : AANonNull(IRP) {}
14381582
14441588 AANonNull::initialize(A);
14451589 }
14461590
1591 /// See AAFromMustBeExecutedContext
1592 bool followUse(Attributor &A, const Use *U, const Instruction *I) {
1593 bool IsNonNull = false;
1594 bool TrackUse = false;
1595 getKnownNonNullAndDerefBytesForUse(A, *this, getAssociatedValue(), U, I,
1596 IsNonNull, TrackUse);
1597 takeKnownMaximum(IsNonNull);
1598 return TrackUse;
1599 }
1600
14471601 /// See AbstractAttribute::getAsStr().
14481602 const std::string getAsStr() const override {
14491603 return getAssumed() ? "nonnull" : "may-null";
14511605 };
14521606
14531607 /// NonNull attribute for a floating value.
1454 struct AANonNullFloating : AANonNullImpl {
1455 AANonNullFloating(const IRPosition &IRP) : AANonNullImpl(IRP) {}
1608 struct AANonNullFloating
1609 : AAFromMustBeExecutedContext {
1610 using Base = AAFromMustBeExecutedContext;
1611 AANonNullFloating(const IRPosition &IRP) : Base(IRP) {}
14561612
14571613 /// See AbstractAttribute::initialize(...).
14581614 void initialize(Attributor &A) override {
1459 AANonNullImpl::initialize(A);
1615 Base::initialize(A);
14601616
14611617 if (isAtFixpoint())
14621618 return;
14741630
14751631 /// See AbstractAttribute::updateImpl(...).
14761632 ChangeStatus updateImpl(Attributor &A) override {
1633 ChangeStatus Change = Base::updateImpl(A);
1634 if (isKnownNonNull())
1635 return Change;
1636
14771637 const DataLayout &DL = A.getDataLayout();
14781638
14791639 auto VisitValueCB = [&](Value &V, AAAlign::StateType &T,
15171677
15181678 /// NonNull attribute for function argument.
15191679 struct AANonNullArgument final
1520 : AAArgumentFromCallSiteArguments {
1680 : AAArgumentFromCallSiteArgumentsAndMustBeExecutedContext
1681 AANonNullImpl> {
15211682 AANonNullArgument(const IRPosition &IRP)
1522 : AAArgumentFromCallSiteArguments(IRP) {}
1683 : AAArgumentFromCallSiteArgumentsAndMustBeExecutedContext
1684 AANonNullImpl>(
1685 IRP) {}
15231686
15241687 /// See AbstractAttribute::trackStatistics()
15251688 void trackStatistics() const override { STATS_DECLTRACK_ARG_ATTR(nonnull) }
15341697
15351698 /// NonNull attribute for a call site return position.
15361699 struct AANonNullCallSiteReturned final
1537 : AACallSiteReturnedFromReturned {
1700 : AACallSiteReturnedFromReturnedAndMustBeExecutedContext
1701 AANonNullImpl> {
15381702 AANonNullCallSiteReturned(const IRPosition &IRP)
1539 : AACallSiteReturnedFromReturned(IRP) {}
1703 : AACallSiteReturnedFromReturnedAndMustBeExecutedContext
1704 AANonNullImpl>(
1705 IRP) {}
15401706
15411707 /// See AbstractAttribute::trackStatistics()
15421708 void trackStatistics() const override { STATS_DECLTRACK_CSRET_ATTR(nonnull) }
22892455 const StateType &getState() const override { return *this; }
22902456 /// }
22912457
2458 /// See AAFromMustBeExecutedContext
2459 bool followUse(Attributor &A, const Use *U, const Instruction *I) {
2460 bool IsNonNull = false;
2461 bool TrackUse = false;
2462 int64_t DerefBytes = getKnownNonNullAndDerefBytesForUse(
2463 A, *this, getAssociatedValue(), U, I, IsNonNull, TrackUse);
2464 takeKnownDerefBytesMaximum(DerefBytes);
2465 return TrackUse;
2466 }
2467
22922468 void getDeducedAttributes(LLVMContext &Ctx,
22932469 SmallVectorImpl &Attrs) const override {
22942470 // TODO: Add *_globally support
23132489 };
23142490
23152491 /// Dereferenceable attribute for a floating value.
2316 struct AADereferenceableFloating : AADereferenceableImpl {
2317 AADereferenceableFloating(const IRPosition &IRP)
2318 : AADereferenceableImpl(IRP) {}
2492 struct AADereferenceableFloating
2493 : AAFromMustBeExecutedContext {
2494 using Base =
2495 AAFromMustBeExecutedContext;
2496 AADereferenceableFloating(const IRPosition &IRP) : Base(IRP) {}
23192497
23202498 /// See AbstractAttribute::updateImpl(...).
23212499 ChangeStatus updateImpl(Attributor &A) override {
2500 ChangeStatus Change = Base::updateImpl(A);
2501
23222502 const DataLayout &DL = A.getDataLayout();
23232503
23242504 auto VisitValueCB = [&](Value &V, DerefState &T, bool Stripped) -> bool {
23772557 A, getIRPosition(), *this, T, VisitValueCB))
23782558 return indicatePessimisticFixpoint();
23792559
2380 return clampStateAndIndicateChange(getState(), T);
2560 return Change | clampStateAndIndicateChange(getState(), T);
23812561 }
23822562
23832563 /// See AbstractAttribute::trackStatistics()
24022582
24032583 /// Dereferenceable attribute for an argument
24042584 struct AADereferenceableArgument final
2405 : AAArgumentFromCallSiteArguments
2406 DerefState> {
2407 AADereferenceableArgument(const IRPosition &IRP)
2408 : AAArgumentFromCallSiteArguments
2409 AADereferenceableImpl, DerefState>(
2410 IRP) {}
2585 : AAArgumentFromCallSiteArgumentsAndMustBeExecutedContext<
2586 AADereferenceable, AADereferenceableImpl, DerefState> {
2587 using Base = AAArgumentFromCallSiteArgumentsAndMustBeExecutedContext<
2588 AADereferenceable, AADereferenceableImpl, DerefState>;
2589 AADereferenceableArgument(const IRPosition &IRP) : Base(IRP) {}
24112590
24122591 /// See AbstractAttribute::trackStatistics()
24132592 void trackStatistics() const override {
24272606 };
24282607
24292608 /// Dereferenceable attribute deduction for a call site return value.
2430 struct AADereferenceableCallSiteReturned final : AADereferenceableImpl {
2431 AADereferenceableCallSiteReturned(const IRPosition &IRP)
2432 : AADereferenceableImpl(IRP) {}
2609 struct AADereferenceableCallSiteReturned final
2610 : AACallSiteReturnedFromReturnedAndMustBeExecutedContext<
2611 AADereferenceable, AADereferenceableImpl> {
2612 using Base = AACallSiteReturnedFromReturnedAndMustBeExecutedContext<
2613 AADereferenceable, AADereferenceableImpl>;
2614 AADereferenceableCallSiteReturned(const IRPosition &IRP) : Base(IRP) {}
24332615
24342616 /// See AbstractAttribute::initialize(...).
24352617 void initialize(Attributor &A) override {
2436 AADereferenceableImpl::initialize(A);
2618 Base::initialize(A);
24372619 Function *F = getAssociatedFunction();
24382620 if (!F)
24392621 indicatePessimisticFixpoint();
24452627 // call site specific liveness information and then it makes
24462628 // sense to specialize attributes for call sites arguments instead of
24472629 // redirecting requests to the callee argument.
2630
2631 ChangeStatus Change = Base::updateImpl(A);
24482632 Function *F = getAssociatedFunction();
24492633 const IRPosition &FnPos = IRPosition::returned(*F);
24502634 auto &FnAA = A.getAAFor(*this, FnPos);
2451 return clampStateAndIndicateChange(
2452 getState(), static_cast(FnAA.getState()));
2635 return Change |
2636 clampStateAndIndicateChange(
2637 getState(), static_cast(FnAA.getState()));
24532638 }
24542639
24552640 /// See AbstractAttribute::trackStatistics()
174174
175175 ; FIXME: This will work with an upcoming patch (D66618 or similar)
176176 ; define align 32 i32* @test10a(i32* align 32 "no-capture-maybe-returned" %p)
177 ; ATTRIBUTOR: define i32* @test10a(i32* align 32 "no-capture-maybe-returned" %p)
177 ; ATTRIBUTOR: define i32* @test10a(i32* nonnull align 32 dereferenceable(4) "no-capture-maybe-returned" %p)
178178 define i32* @test10a(i32* align 32 %p) {
179179 ; ATTRIBUTOR: %l = load i32, i32* %p, align 32
180180 %l = load i32, i32* %p
202202
203203 ; FIXME: This will work with an upcoming patch (D66618 or similar)
204204 ; define align 32 i32* @test10b(i32* align 32 "no-capture-maybe-returned" %p)
205 ; ATTRIBUTOR: define i32* @test10b(i32* align 32 "no-capture-maybe-returned" %p)
205 ; ATTRIBUTOR: define i32* @test10b(i32* nonnull align 32 dereferenceable(4) "no-capture-maybe-returned" %p)
206206 define i32* @test10b(i32* align 32 %p) {
207207 ; ATTRIBUTOR: %l = load i32, i32* %p, align 32
208208 %l = load i32, i32* %p
243243 ; }
244244 ;
245245 ; There should *not* be a no-capture attribute on %a
246 ; CHECK: define i64* @not_captured_but_returned_0(i64* returned writeonly "no-capture-maybe-returned" %a)
246 ; CHECK: define nonnull dereferenceable(8) i64* @not_captured_but_returned_0(i64* nonnull returned writeonly dereferenceable(8) "no-capture-maybe-returned" %a)
247
247248 define i64* @not_captured_but_returned_0(i64* %a) #0 {
248249 entry:
249250 store i64 0, i64* %a, align 8
353354 ;
354355 ; CHECK: define i32* @ret_arg_or_unknown(i32* readnone %b)
355356 ; CHECK: define i32* @ret_arg_or_unknown_through_phi(i32* readnone %b)
357
356358 declare i32* @unknown()
357359
358360 define i32* @ret_arg_or_unknown(i32* %b) #0 {
253253 ;
254254 ; FNATTR: define i32* @rt0(i32* readonly %a)
255255 ; BOTH: Function Attrs: nofree noinline noreturn nosync nounwind readonly uwtable
256 ; BOTH-NEXT: define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @rt0(i32* nocapture readonly %a)
256 ; BOTH-NEXT: define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @rt0(i32* nocapture nonnull readonly dereferenceable(4) %a)
257257 define i32* @rt0(i32* %a) #0 {
258258 entry:
259259 %v = load i32, i32* %a, align 4
271271 ;
272272 ; FNATTR: define noalias i32* @rt1(i32* nocapture readonly %a)
273273 ; BOTH: Function Attrs: nofree noinline noreturn nosync nounwind readonly uwtable
274 ; BOTH-NEXT: define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @rt1(i32* nocapture readonly %a)
274 ; BOTH-NEXT: define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @rt1(i32* nocapture nonnull readonly dereferenceable(4) %a)
275275 define i32* @rt1(i32* %a) #0 {
276276 entry:
277277 %v = load i32, i32* %a, align 4
0 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
1 ; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s
1 ; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=2 < %s | FileCheck %s
22 ; ModuleID = 'callback_simple.c'
33 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
44
1313 ; FIXME: The callee -> call site direction is not working yet.
1414
1515 define void @t0_caller(i32* %a) {
16 ; CHECK: @t0_caller(i32* [[A:%.*]])
16 ; CHECK-LABEL: @t0_caller(
1717 ; CHECK-NEXT: entry:
1818 ; CHECK-NEXT: [[B:%.*]] = alloca i32, align 32
1919 ; CHECK-NEXT: [[C:%.*]] = alloca i32*, align 64
3838 ; Note that the first two arguments are provided by the callback_broker according to the callback in !1 below!
3939 ; The others are annotated with alignment information, amongst others, or even replaced by the constants passed to the call.
4040 define internal void @t0_callback_callee(i32* %is_not_null, i32* %ptr, i32* %a, i64 %b, i32** %c) {
41 ; CHECK: @t0_callback_callee(i32* nocapture writeonly [[IS_NOT_NULL:%.*]], i32* nocapture readonly [[PTR:%.*]], i32* [[A:%.*]], i64 [[B:%.*]], i32** nocapture nonnull readonly align 64 dereferenceable(8) [[C:%.*]])
41 ; CHECK-LABEL: @t0_callback_callee(
4242 ; CHECK-NEXT: entry:
4343 ; CHECK-NEXT: [[PTR_VAL:%.*]] = load i32, i32* [[PTR:%.*]], align 8
4444 ; CHECK-NEXT: store i32 [[PTR_VAL]], i32* [[IS_NOT_NULL:%.*]]
None ; RUN: opt -attributor -attributor-manifest-internal --attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR
0 ; RUN: opt -attributor -attributor-manifest-internal --attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR
11
22
33 declare void @deref_phi_user(i32* %a);
110110 for.end: ; preds = %for.cond.cleanup
111111 ret void
112112 }
113
114 ; TEST 7
115 ; share known infomation in must-be-executed-context
116 declare i32* @unkown_ptr() willreturn nounwind
117 declare i32 @unkown_f(i32*) willreturn nounwind
118 define i32* @f7_0(i32* %ptr) {
119 ; ATTRIBUTOR: define nonnull dereferenceable(8) i32* @f7_0(i32* nonnull returned dereferenceable(8) %ptr)
120 %T = tail call i32 @unkown_f(i32* dereferenceable(8) %ptr)
121 ret i32* %ptr
122 }
123
124 ; ATTRIBUTOR: define void @f7_1(i32* nonnull dereferenceable(4) %ptr, i1 %c)
125 define void @f7_1(i32* %ptr, i1 %c) {
126
127 ; ATTRIBUTOR: %A = tail call i32 @unkown_f(i32* nonnull dereferenceable(4) %ptr)
128 %A = tail call i32 @unkown_f(i32* %ptr)
129
130 %ptr.0 = load i32, i32* %ptr
131 ; deref 4 hold
132
133 ; FIXME: this should be %B = tail call i32 @unkown_f(i32* nonnull dereferenceable(4) %ptr)
134 ; ATTRIBUTOR: %B = tail call i32 @unkown_f(i32* nonnull dereferenceable(4) %ptr)
135 %B = tail call i32 @unkown_f(i32* dereferenceable(1) %ptr)
136
137 br i1%c, label %if.true, label %if.false
138 if.true:
139 ; ATTRIBUTOR: %C = tail call i32 @unkown_f(i32* nonnull dereferenceable(8) %ptr)
140 %C = tail call i32 @unkown_f(i32* %ptr)
141
142 ; ATTRIBUTOR: %D = tail call i32 @unkown_f(i32* nonnull dereferenceable(8) %ptr)
143 %D = tail call i32 @unkown_f(i32* dereferenceable(8) %ptr)
144
145 ; FIXME: This should be tail call i32 @unkown_f(i32* nonnull dereferenceable(8) %ptr)
146 ; Making must-be-executed-context backward exploration will fix this.
147 ; ATTRIBUTOR: %E = tail call i32 @unkown_f(i32* nonnull dereferenceable(4) %ptr)
148 %E = tail call i32 @unkown_f(i32* %ptr)
149
150 ret void
151
152 if.false:
153 ret void
154 }
155
156 ; ATTRIBUTOR: define void @f7_2(i1 %c)
157 define void @f7_2(i1 %c) {
158
159 %ptr = tail call i32* @unkown_ptr()
160
161 ; ATTRIBUTOR: %A = tail call i32 @unkown_f(i32* nonnull dereferenceable(4) %ptr)
162 %A = tail call i32 @unkown_f(i32* %ptr)
163
164 %arg_a.0 = load i32, i32* %ptr
165 ; deref 4 hold
166
167 ; ATTRIBUTOR: %B = tail call i32 @unkown_f(i32* nonnull dereferenceable(4) %ptr)
168 %B = tail call i32 @unkown_f(i32* dereferenceable(1) %ptr)
169
170 br i1%c, label %if.true, label %if.false
171 if.true:
172
173 ; ATTRIBUTOR: %C = tail call i32 @unkown_f(i32* nonnull dereferenceable(8) %ptr)
174 %C = tail call i32 @unkown_f(i32* %ptr)
175
176 ; ATTRIBUTOR: %D = tail call i32 @unkown_f(i32* nonnull dereferenceable(8) %ptr)
177 %D = tail call i32 @unkown_f(i32* dereferenceable(8) %ptr)
178
179 %E = tail call i32 @unkown_f(i32* %ptr)
180 ; FIXME: This should be @unkown_f(i32* nonnull dereferenceable(8) %ptr)
181 ; Making must-be-executed-context backward exploration will fix this.
182 ; ATTRIBUTOR: %E = tail call i32 @unkown_f(i32* nonnull dereferenceable(4) %ptr)
183
184 ret void
185
186 if.false:
187 ret void
188 }
189
190 define i32* @f7_3() {
191 ; ATTRIBUTOR: define nonnull dereferenceable(4) i32* @f7_3()
192 %ptr = tail call i32* @unkown_ptr()
193 store i32 10, i32* %ptr, align 16
194 ret i32* %ptr
195 }
196
197 define i32* @test_for_minus_index(i32* %p) {
198 ; FIXME: This should be define nonnull dereferenceable(8) i32* @test_for_minus_index(i32* nonnull %p)
199 ; ATTRIBUTOR: define nonnull dereferenceable(8) i32* @test_for_minus_index(i32* writeonly "no-capture-maybe-returned" %p)
200 %q = getelementptr inbounds i32, i32* %p, i32 -2
201 store i32 1, i32* %q
202 ret i32* %q
203 }
77 ret i32 %add
88 }
99
10 ; CHECK: define private i32 @noalias_args(i32* nocapture readonly %A, i32* noalias nocapture readonly %B)
10 ; CHECK: define private i32 @noalias_args(i32* nocapture nonnull readonly dereferenceable(4) %A, i32* noalias nocapture nonnull readonly dereferenceable(4) %B)
1111
1212 define private i32 @noalias_args(i32* %A, i32* %B) #0 {
1313 entry:
2222
2323 ; FIXME: Should be something like this.
2424 ; define internal i32 @noalias_args_argmem(i32* noalias nocapture readonly %A, i32* noalias nocapture readonly %B)
25 ; CHECK: define internal i32 @noalias_args_argmem(i32* nocapture readonly %A, i32* nocapture readonly %B)
25 ; CHECK: define internal i32 @noalias_args_argmem(i32* nocapture nonnull readonly dereferenceable(4) %A, i32* nocapture nonnull readonly dereferenceable(4) %B)
26
2627 ;
2728 define internal i32 @noalias_args_argmem(i32* %A, i32* %B) #1 {
2829 entry:
3939 }
4040
4141 ; CHECK: Function Attrs: nofree norecurse nosync nounwind readonly uwtable willreturn
42 ; CHECK-NEXT: define internal i32 @internal_load(i32* nocapture nonnull readonly %0)
42 ; CHECK-NEXT: define internal i32 @internal_load(i32* nocapture nonnull readonly dereferenceable(4) %0)
4343 define internal i32 @internal_load(i32*) norecurse nounwind uwtable {
4444 %2 = load i32, i32* %0, align 4
4545 ret i32 %2
None ; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s
0 ; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=2 < %s | FileCheck %s
11
22 ; TEST 1 - negative.
33
119119 ret void
120120 }
121121
122 ; EITHER: define void @nc3(void ()* nocapture %p)
122
123 ; FNATTR: define void @nc3(void ()* nocapture %p)
124 ; ATTRIBUTOR: define void @nc3(void ()* nocapture nonnull %p)
123125 define void @nc3(void ()* %p) {
124126 call void %p()
125127 ret void
132134 ret void
133135 }
134136
135 ; EITHER: define void @nc5(void (i8*)* nocapture %f, i8* nocapture %p)
137 ; FNATTR: define void @nc5(void (i8*)* nocapture %f, i8* nocapture %p)
138 ; ATTRIBUTOR: define void @nc5(void (i8*)* nocapture nonnull %f, i8* nocapture %p)
136139 define void @nc5(void (i8*)* %f, i8* %p) {
137140 call void %f(i8* %p) readonly nounwind
138141 call void %f(i8* nocapture %p)
212215 ret void
213216 }
214217
215 ; EITHER: define void @test_cmpxchg(i32* nocapture %p)
218 ; FNATTR: define void @test_cmpxchg(i32* nocapture %p)
219 ; ATTRIBUTOR: define void @test_cmpxchg(i32* nocapture nonnull dereferenceable(4) %p)
216220 define void @test_cmpxchg(i32* %p) {
217221 cmpxchg i32* %p, i32 0, i32 1 acquire monotonic
218222 ret void
219223 }
220224
221 ; EITHER: define void @test_cmpxchg_ptr(i32** nocapture %p, i32* %q)
225 ; FNATTR: define void @test_cmpxchg_ptr(i32** nocapture %p, i32* %q)
226 ; ATTRIBUTOR: define void @test_cmpxchg_ptr(i32** nocapture nonnull dereferenceable(8) %p, i32* %q)
222227 define void @test_cmpxchg_ptr(i32** %p, i32* %q) {
223228 cmpxchg i32** %p, i32* null, i32* %q acquire monotonic
224229 ret void
225230 }
226231
227 ; EITHER: define void @test_atomicrmw(i32* nocapture %p)
232 ; FNATTR: define void @test_atomicrmw(i32* nocapture %p)
233 ; ATTRIBUTOR: define void @test_atomicrmw(i32* nocapture nonnull dereferenceable(4) %p)
228234 define void @test_atomicrmw(i32* %p) {
229235 atomicrmw add i32* %p, i32 1 seq_cst
230236 ret void
235235 ret void
236236 }
237237
238 declare void @fun0() #1
239 declare void @fun1(i8*) #1
240 declare void @fun2(i8*, i8*) #1
241 declare void @fun3(i8*, i8*, i8*) #1
242 ; TEST 16 simple path test
243 ; if(..)
244 ; fun2(nonnull %a, nonnull %b)
245 ; else
246 ; fun2(nonnull %a, %b)
247 ; We can say that %a is nonnull but %b is not.
248 define void @f16(i8* %a, i8 * %b, i8 %c) {
249 ; FIXME: missing nonnull on %a
250 ; ATTRIBUTOR: define void @f16(i8* %a, i8* %b, i8 %c)
251 %cmp = icmp eq i8 %c, 0
252 br i1 %cmp, label %if.then, label %if.else
253 if.then:
254 tail call void @fun2(i8* nonnull %a, i8* nonnull %b)
255 ret void
256 if.else:
257 tail call void @fun2(i8* nonnull %a, i8* %b)
258 ret void
259 }
260 ; TEST 17 explore child BB test
261 ; if(..)
262 ; ... (willreturn & nounwind)
263 ; else
264 ; ... (willreturn & nounwind)
265 ; fun1(nonnull %a)
266 ; We can say that %a is nonnull
267 define void @f17(i8* %a, i8 %c) {
268 ; FIXME: missing nonnull on %a
269 ; ATTRIBUTOR: define void @f17(i8* %a, i8 %c)
270 %cmp = icmp eq i8 %c, 0
271 br i1 %cmp, label %if.then, label %if.else
272 if.then:
273 tail call void @fun0()
274 br label %cont
275 if.else:
276 tail call void @fun0()
277 br label %cont
278 cont:
279 tail call void @fun1(i8* nonnull %a)
280 ret void
281 }
282 ; TEST 18 More complex test
283 ; if(..)
284 ; ... (willreturn & nounwind)
285 ; else
286 ; ... (willreturn & nounwind)
287 ; if(..)
288 ; ... (willreturn & nounwind)
289 ; else
290 ; ... (willreturn & nounwind)
291 ; fun1(nonnull %a)
292
293 define void @f18(i8* %a, i8* %b, i8 %c) {
294 ; FIXME: missing nonnull on %a
295 ; ATTRIBUTOR: define void @f18(i8* %a, i8* %b, i8 %c)
296 %cmp1 = icmp eq i8 %c, 0
297 br i1 %cmp1, label %if.then, label %if.else
298 if.then:
299 tail call void @fun0()
300 br label %cont
301 if.else:
302 tail call void @fun0()
303 br label %cont
304 cont:
305 %cmp2 = icmp eq i8 %c, 1
306 br i1 %cmp2, label %cont.then, label %cont.else
307 cont.then:
308 tail call void @fun1(i8* nonnull %b)
309 br label %cont2
310 cont.else:
311 tail call void @fun0()
312 br label %cont2
313 cont2:
314 tail call void @fun1(i8* nonnull %a)
315 ret void
316 }
317
318 ; TEST 19: Loop
319
320 define void @f19(i8* %a, i8* %b, i8 %c) {
321 ; FIXME: missing nonnull on %b
322 ; ATTRIBUTOR: define void @f19(i8* %a, i8* %b, i8 %c)
323 br label %loop.header
324 loop.header:
325 %cmp2 = icmp eq i8 %c, 0
326 br i1 %cmp2, label %loop.body, label %loop.exit
327 loop.body:
328 tail call void @fun1(i8* nonnull %b)
329 tail call void @fun1(i8* nonnull %a)
330 br label %loop.header
331 loop.exit:
332 tail call void @fun1(i8* nonnull %b)
333 ret void
334 }
335
238336 ; Test propagation of nonnull callsite args back to caller.
239337
240338 declare void @use1(i8* %x)
267365 ; FNATTR-NEXT: call void @use3nonnull(i8* %b, i8* %c, i8* %a)
268366 ; FNATTR-NEXT: call void @use3(i8* %c, i8* %a, i8* %b)
269367
270 ; FIXME: missing "nonnull", it should be
271 ; @parent2(i8* nonnull %a, i8* nonnull %b, i8* nonnull %c)
272 ; call void @use3nonnull(i8* nonnull %b, i8* nonnull %c, i8* nonnull %a)
273 ; call void @use3(i8* nonnull %c, i8* nonnull %a, i8* nonnull %b)
274
275 ; ATTRIBUTOR-LABEL: @parent2(i8* %a, i8* %b, i8* %c)
368 ; ATTRIBUTOR-LABEL: @parent2(i8* nonnull %a, i8* nonnull %b, i8* nonnull %c)
276369 ; ATTRIBUTOR-NEXT: call void @use3nonnull(i8* nonnull %b, i8* nonnull %c, i8* nonnull %a)
277 ; ATTRIBUTOR-NEXT: call void @use3(i8* %c, i8* %a, i8* %b)
370 ; ATTRIBUTOR-NEXT: call void @use3(i8* nonnull %c, i8* nonnull %a, i8* nonnull %b)
278371
279372 ; BOTH-NEXT: ret void
280373 call void @use3nonnull(i8* %b, i8* %c, i8* %a)
289382 ; FNATTR-NEXT: call void @use1nonnull(i8* %a)
290383 ; FNATTR-NEXT: call void @use3(i8* %c, i8* %b, i8* %a)
291384
292 ; FIXME: missing "nonnull", it should be,
293 ; @parent3(i8* nonnull %a, i8* %b, i8* %c)
294 ; call void @use1nonnull(i8* nonnull %a)
295 ; call void @use3(i8* %c, i8* %b, i8* nonnull %a)
296 ; ATTRIBUTOR-LABEL: @parent3(i8* %a, i8* %b, i8* %c)
385 ; ATTRIBUTOR-LABEL: @parent3(i8* nonnull %a, i8* %b, i8* %c)
297386 ; ATTRIBUTOR-NEXT: call void @use1nonnull(i8* nonnull %a)
298 ; ATTRIBUTOR-NEXT: call void @use3(i8* %c, i8* %b, i8* %a)
387 ; ATTRIBUTOR-NEXT: call void @use3(i8* %c, i8* %b, i8* nonnull %a)
299388
300389 ; BOTH-NEXT: ret void
301390
312401 ; CHECK-NEXT: call void @use2(i8* %a, i8* %c)
313402 ; CHECK-NEXT: call void @use1(i8* %b)
314403
315 ; FIXME : missing "nonnull", it should be
316 ; @parent4(i8* %a, i8* nonnull %b, i8* nonnull %c)
317 ; call void @use2nonnull(i8* nonnull %c, i8* nonull %b)
318 ; call void @use2(i8* %a, i8* nonnull %c)
319 ; call void @use1(i8* nonnull %b)
320
321 ; ATTRIBUTOR-LABEL: @parent4(i8* %a, i8* %b, i8* %c)
404 ; ATTRIBUTOR-LABEL: @parent4(i8* %a, i8* nonnull %b, i8* nonnull %c)
322405 ; ATTRIBUTOR-NEXT: call void @use2nonnull(i8* nonnull %c, i8* nonnull %b)
323 ; ATTRIBUTOR-NEXT: call void @use2(i8* %a, i8* %c)
324 ; ATTRIBUTOR-NEXT: call void @use1(i8* %b)
406 ; ATTRIBUTOR-NEXT: call void @use2(i8* %a, i8* nonnull %c)
407 ; ATTRIBUTOR-NEXT: call void @use1(i8* nonnull %b)
325408
326409 ; BOTH: ret void
327410
358441
359442 define i8 @parent6(i8* %a, i8* %b) {
360443 ; FNATTR-LABEL: @parent6(i8* nonnull %a, i8* %b)
361 ; FIXME: missing "nonnull"
362 ; ATTRIBUTOR-LABEL: @parent6(i8* %a, i8* %b)
444 ; ATTRIBUTOR-LABEL: @parent6(i8* nonnull %a, i8* %b)
363445 ; BOTH-NEXT: [[C:%.*]] = load volatile i8, i8* %b
364446 ; FNATTR-NEXT: call void @use1nonnull(i8* %a)
365447 ; ATTRIBUTOR-NEXT: call void @use1nonnull(i8* nonnull %a)
377459 ; FNATTR-NEXT: [[RET:%.*]] = call i8 @use1safecall(i8* %a)
378460 ; FNATTR-NEXT: call void @use1nonnull(i8* %a)
379461
380 ; FIXME : missing "nonnull", it should be
381 ; @parent7(i8* nonnull %a)
382 ; [[RET:%.*]] = call i8 @use1safecall(i8* nonnull %a)
383 ; call void @use1nonnull(i8* nonnull %a)
384 ; ret i8 [[RET]]
385
386 ; ATTRIBUTOR-LABEL: @parent7(i8* %a)
387 ; ATTRIBUTOR-NEXT: [[RET:%.*]] = call i8 @use1safecall(i8* %a)
462
463 ; ATTRIBUTOR-LABEL: @parent7(i8* nonnull %a)
464 ; ATTRIBUTOR-NEXT: [[RET:%.*]] = call i8 @use1safecall(i8* nonnull %a)
388465 ; ATTRIBUTOR-NEXT: call void @use1nonnull(i8* nonnull %a)
389466
390467 ; BOTH-NEXT: ret i8 [[RET]]
399476 declare i32 @esfp(...)
400477
401478 define i1 @parent8(i8* %a, i8* %bogus1, i8* %b) personality i8* bitcast (i32 (...)* @esfp to i8*){
402 ; FNATTR-LABEL: @parent8(i8* nonnull %a, i8* nocapture readnone %bogus1, i8* nonnull %b)
403 ; FIXME : missing "nonnull", it should be @parent8(i8* nonnull %a, i8* %bogus1, i8* nonnull %b)
404 ; ATTRIBUTOR-LABEL: @parent8(i8* %a, i8* nocapture readnone %bogus1, i8* %b)
479 ; BOTH-LABEL: @parent8(i8* nonnull %a, i8* nocapture readnone %bogus1, i8* nonnull %b)
405480 ; BOTH-NEXT: entry:
406481 ; FNATTR-NEXT: invoke void @use2nonnull(i8* %a, i8* %b)
407482 ; ATTRIBUTOR-NEXT: invoke void @use2nonnull(i8* nonnull %a, i8* nonnull %b)
469544 ret void
470545 }
471546
547
472548 attributes #0 = { "null-pointer-is-valid"="true" }
549 attributes #1 = { nounwind willreturn}
129129
130130 ; Call through a function pointer
131131 ; ATTRIBUTOR-NOT: Function Attrs
132 ; ATTRIBUTOR: define i32 @eval_func(i32 (i32)* nocapture %0, i32 %1)
133 define i32 @eval_func(i32 (i32)* , i32) local_unnamed_addr {
132 ; ATTRIBUTOR: define i32 @eval_func1(i32 (i32)* nocapture nonnull %0, i32 %1)
133 define i32 @eval_func1(i32 (i32)* , i32) local_unnamed_addr {
134 %3 = tail call i32 %0(i32 %1) #2
135 ret i32 %3
136 }
137
138 ; ATTRIBUTOR-NOT: Function Attrs
139 ; ATTRIBUTOR: define i32 @eval_func2(i32 (i32)* nocapture %0, i32 %1)
140 define i32 @eval_func2(i32 (i32)* , i32) local_unnamed_addr "null-pointer-is-valid"="true"{
134141 %3 = tail call i32 %0(i32 %1) #2
135142 ret i32 %3
136143 }
4444 ; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
4545 ; FNATTR-NEXT: define i32 @load_monotonic(i32* nocapture readonly %0)
4646 ; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind uwtable
47 ; ATTRIBUTOR-NEXT: define i32 @load_monotonic(i32* nocapture readonly %0)
47 ; ATTRIBUTOR-NEXT: define i32 @load_monotonic(i32* nocapture nonnull readonly dereferenceable(4) %0)
4848 define i32 @load_monotonic(i32* nocapture readonly %0) norecurse nounwind uwtable {
4949 %2 = load atomic i32, i32* %0 monotonic, align 4
5050 ret i32 %2
6060 ; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
6161 ; FNATTR-NEXT: define void @store_monotonic(i32* nocapture %0)
6262 ; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind uwtable
63 ; ATTRIBUTOR-NEXT: define void @store_monotonic(i32* nocapture writeonly %0)
63 ; ATTRIBUTOR-NEXT: define void @store_monotonic(i32* nocapture nonnull writeonly dereferenceable(4) %0)
6464 define void @store_monotonic(i32* nocapture %0) norecurse nounwind uwtable {
6565 store atomic i32 10, i32* %0 monotonic, align 4
6666 ret void
7777 ; FNATTR-NEXT: define i32 @load_acquire(i32* nocapture readonly %0)
7878 ; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable
7979 ; ATTRIBUTOR-NOT: nosync
80 ; ATTRIBUTOR-NEXT: define i32 @load_acquire(i32* nocapture readonly %0)
80 ; ATTRIBUTOR-NEXT: define i32 @load_acquire(i32* nocapture nonnull readonly dereferenceable(4) %0)
8181 define i32 @load_acquire(i32* nocapture readonly %0) norecurse nounwind uwtable {
8282 %2 = load atomic i32, i32* %0 acquire, align 4
8383 ret i32 %2
223223 ; FNATTR: Function Attrs: nofree norecurse nounwind
224224 ; FNATTR-NEXT: define void @foo1(i32* nocapture %0, %"struct.std::atomic"* nocapture %1)
225225 ; ATTRIBUTOR-NOT: nosync
226 ; ATTRIBUTOR: define void @foo1(i32* nocapture writeonly %0, %"struct.std::atomic"* nocapture writeonly %1)
226 ; ATTRIBUTOR: define void @foo1(i32* nocapture nonnull writeonly dereferenceable(4) %0, %"struct.std::atomic"* nocapture writeonly %1)
227
227228 define void @foo1(i32* %0, %"struct.std::atomic"* %1) {
228229 store i32 100, i32* %0, align 4
229230 fence release
254255 ; TEST 13 - Fence syncscope("singlethread") seq_cst
255256 ; FNATTR: Function Attrs: nofree norecurse nounwind
256257 ; FNATTR-NEXT: define void @foo1_singlethread(i32* nocapture %0, %"struct.std::atomic"* nocapture %1)
257 ; ATTRIBUTOR: Function Attrs: nofree nosync
258 ; ATTRIBUTOR: define void @foo1_singlethread(i32* nocapture writeonly %0, %"struct.std::atomic"* nocapture writeonly %1)
258 ; ATTRIBUTOR: Function Attrs: nofree nosync nounwind willreturn
259 ; ATTRIBUTOR: define void @foo1_singlethread(i32* nocapture nonnull writeonly dereferenceable(4) %0, %"struct.std::atomic"* nocapture writeonly %1)
260
259261 define void @foo1_singlethread(i32* %0, %"struct.std::atomic"* %1) {
260262 store i32 100, i32* %0, align 4
261263 fence syncscope("singlethread") release
266268
267269 ; FNATTR: Function Attrs: nofree norecurse nounwind
268270 ; FNATTR-NEXT: define void @bar_singlethread(i32* nocapture readnone %0, %"struct.std::atomic"* nocapture readonly %1)
269 ; ATTRIBUTOR: Function Attrs: nofree nosync
271 ; ATTRIBUTOR: Function Attrs: nofree nosync nounwind
270272 ; ATTRIBUTOR: define void @bar_singlethread(i32* nocapture readnone %0, %"struct.std::atomic"* nocapture readonly %1)
271273 define void @bar_singlethread(i32* %0, %"struct.std::atomic"* %1) {
272274 %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
6969 }
7070
7171 ; CHECK: Function Attrs: nofree nosync nounwind
72 ; CHECK-NEXT: define internal i32* @internal_ret1_rrw(i32* %r0, i32* returned %r1, i32* %w0)
72 ; CHECK-NEXT: define internal i32* @internal_ret1_rrw(i32* nonnull dereferenceable(4) %r0, i32* returned %r1, i32* %w0)
7373 define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) {
7474 entry:
7575 %0 = load i32, i32* %r0, align 4
120120 }
121121
122122 ; CHECK: Function Attrs: nofree nosync nounwind
123 ; CHECK-NEXT: define internal i32* @internal_ret1_rw(i32* %r0, i32* returned %w0)
123 ; CHECK-NEXT: define internal i32* @internal_ret1_rw(i32* nonnull dereferenceable(4) %r0, i32* returned %w0)
124124 define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) {
125125 entry:
126126 %0 = load i32, i32* %r0, align 4
3838 }
3939
4040 ; FNATTR: define void @test5(i8** nocapture %p, i8* %q)
41 ; ATTRIBUTOR: define void @test5(i8** nocapture writeonly %p, i8* %q)
41 ; ATTRIBUTOR: define void @test5(i8** nocapture nonnull writeonly dereferenceable(8) %p, i8* %q)
4242 ; Missed optz'n: we could make %q readnone, but don't break test6!
4343 define void @test5(i8** %p, i8* %q) {
4444 store i8* %q, i8** %p
4747
4848 declare void @test6_1()
4949 ; FNATTR: define void @test6_2(i8** nocapture %p, i8* %q)
50 ; ATTRIBUTOR: define void @test6_2(i8** nocapture writeonly %p, i8* %q)
50 ; ATTRIBUTOR: define void @test6_2(i8** nocapture nonnull writeonly dereferenceable(8) %p, i8* %q)
5151 ; This is not a missed optz'n.
5252 define void @test6_2(i8** %p, i8* %q) {
5353 store i8* %q, i8** %p
0 ; RUN: opt < %s -inferattrs -S | FileCheck %s
1 ; RUN: opt < %s -attributor --attributor-disable=false -S | FileCheck %s --check-prefix=ATTRIBUTOR
2
3
14
25 ; Determine dereference-ability before unused loads get deleted:
36 ; https://bugs.llvm.org/show_bug.cgi?id=21780
47
58 define <4 x double> @PR21780(double* %ptr) {
69 ; CHECK-LABEL: @PR21780(double* %ptr)
10 ; FIXME: this should be @PR21780(double* nonnull dereferenceable(32) %ptr)
11 ; trakcing use of GEP in Attributor would fix this problem.
12 ; ATTRIBUTOR-LABEL: @PR21780(double* nocapture nonnull readonly dereferenceable(8) %ptr)
13
714 ; GEP of index 0 is simplified away.
815 %arrayidx1 = getelementptr inbounds double, double* %ptr, i64 1
916 %arrayidx2 = getelementptr inbounds double, double* %ptr, i64 2
2027 %vecinit3 = insertelement <4 x double> %vecinit2, double %t3, i32 3
2128 %shuffle = shufflevector <4 x double> %vecinit3, <4 x double> %vecinit3, <4 x i32>
2229 ret <4 x double> %shuffle
30 }
31
32
33 define double @PR21780_only_access3_with_inbounds(double* %ptr) {
34 ; CHECK-LABEL: @PR21780_only_access3_with_inbounds(double* %ptr)
35 ; FIXME: this should be @PR21780_only_access3_with_inbounds(double* nonnull dereferenceable(32) %ptr)
36 ; trakcing use of GEP in Attributor would fix this problem.
37 ; ATTRIBUTOR-LABEL: @PR21780_only_access3_with_inbounds(double* nocapture readonly %ptr)
38
39 %arrayidx3 = getelementptr inbounds double, double* %ptr, i64 3
40 %t3 = load double, double* %arrayidx3, align 8
41 ret double %t3
42 }
43
44 define double @PR21780_only_access3_without_inbounds(double* %ptr) {
45 ; CHECK-LABEL: @PR21780_only_access3_without_inbounds(double* %ptr)
46 ; ATTRIBUTOR-LABEL: @PR21780_only_access3_without_inbounds(double* nocapture readonly %ptr)
47 %arrayidx3 = getelementptr double, double* %ptr, i64 3
48 %t3 = load double, double* %arrayidx3, align 8
49 ret double %t3
50 }
51
52 define double @PR21780_without_inbounds(double* %ptr) {
53 ; CHECK-LABEL: @PR21780_without_inbounds(double* %ptr)
54 ; FIXME: this should be @PR21780_without_inbounds(double* nonnull dereferenceable(32) %ptr)
55 ; ATTRIBUTOR-LABEL: @PR21780_without_inbounds(double* nocapture nonnull readonly dereferenceable(8) %ptr)
56
57 %arrayidx1 = getelementptr double, double* %ptr, i64 1
58 %arrayidx2 = getelementptr double, double* %ptr, i64 2
59 %arrayidx3 = getelementptr double, double* %ptr, i64 3
60
61 %t0 = load double, double* %ptr, align 8
62 %t1 = load double, double* %arrayidx1, align 8
63 %t2 = load double, double* %arrayidx2, align 8
64 %t3 = load double, double* %arrayidx3, align 8
65
66 ret double %t3
2367 }
2468
2569 ; Unsimplified, but still valid. Also, throw in some bogus arguments.