diff --git a/llvm/include/llvm/Analysis/MustExecute.h b/llvm/include/llvm/Analysis/MustExecute.h --- a/llvm/include/llvm/Analysis/MustExecute.h +++ b/llvm/include/llvm/Analysis/MustExecute.h @@ -33,8 +33,13 @@ namespace llvm { +namespace { +template using GetterTy = std::function; +} + class Instruction; class DominatorTree; +class PostDominatorTree; class Loop; /// Captures loop safety information. @@ -374,8 +379,14 @@ /// \param ExploreInterBlock Flag to indicate if instructions in blocks /// other than the parent of PP should be /// explored. - MustBeExecutedContextExplorer(bool ExploreInterBlock) - : ExploreInterBlock(ExploreInterBlock), EndIterator(*this, nullptr) {} + MustBeExecutedContextExplorer( + bool ExploreInterBlock, + GetterTy LIGetter = + [](const Function &) { return nullptr; }, + GetterTy PDTGetter = + [](const Function &) { return nullptr; }) + : ExploreInterBlock(ExploreInterBlock), LIGetter(LIGetter), + PDTGetter(PDTGetter), EndIterator(*this, nullptr) {} /// Clean up the dynamically allocated iterators. ~MustBeExecutedContextExplorer() { @@ -454,6 +465,9 @@ getMustBeExecutedNextInstruction(MustBeExecutedIterator &It, const Instruction *PP); + /// Find the next join point from \p InitBB in forward direction. + const BasicBlock *findForwardJoinPoint(const BasicBlock *InitBB); + /// Parameter that limit the performed exploration. See the constructor for /// their meaning. ///{ @@ -461,6 +475,19 @@ ///} private: + /// Getters for common CFG analyses: LoopInfo, DominatorTree, and + /// PostDominatorTree. + ///{ + GetterTy LIGetter; + GetterTy PDTGetter; + ///} + + /// Map to cache isGuaranteedToTransferExecutionToSuccessor results. + DenseMap> BlockTransferMap; + + /// Map to cache containsIrreducibleCFG results. + DenseMap> IrreducibleControlMap; + /// Map from instructions to associated must be executed iterators. DenseMap InstructionIteratorMap; diff --git a/llvm/lib/Analysis/MustExecute.cpp b/llvm/lib/Analysis/MustExecute.cpp --- a/llvm/lib/Analysis/MustExecute.cpp +++ b/llvm/lib/Analysis/MustExecute.cpp @@ -13,6 +13,7 @@ #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/Passes.h" #include "llvm/Analysis/ValueTracking.h" +#include "llvm/Analysis/PostDominators.h" #include "llvm/IR/AssemblyAnnotationWriter.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/InstIterator.h" @@ -353,7 +354,19 @@ } bool MustBeExecutedContextPrinter::runOnModule(Module &M) { - MustBeExecutedContextExplorer Explorer(true); + // We provide non-PM analysis here because the old PM doesn't like to query + // function passes from a module pass. Given that this is a printer, we don't + // care much about memory leaks. + GetterTy LIGetter = [this](const Function &F) { + DominatorTree *DT = new DominatorTree(const_cast(F)); + LoopInfo *LI = new LoopInfo(*DT); + return LI; + }; + GetterTy PDTGetter = [this](const Function &F) { + PostDominatorTree *PDT = new PostDominatorTree(const_cast(F)); + return PDT; + }; + MustBeExecutedContextExplorer Explorer(true, LIGetter, PDTGetter); for (Function &F : M) { for (Instruction &I : instructions(F)) { dbgs() << "-- Explore context of: " << I << "\n"; @@ -443,6 +456,173 @@ return false; } +/// Return true if \p L might be an endless loop. +static bool maybeEndlessLoop(const Loop &L) { + if (L.getHeader()->getParent()->hasFnAttribute(Attribute::WillReturn)) + return false; + // TODO: Actually try to prove it is not. + // TODO: If maybeEndlessLoop is going to be expensive, cache it. + return true; +} + +static bool mayContainIrreducibleControl(const Function &F, const LoopInfo *LI) { + if (!LI) + return false; + using RPOTraversal = ReversePostOrderTraversal; + RPOTraversal FuncRPOT(&F); + return !containsIrreducibleCFG(FuncRPOT, *LI); +} + +/// Lookup \p Key in \p Map and return the result, potentially after +/// initializing the optional through \p Fn(\p args). +template +static V getOrCreateCachedOptional(K Key, DenseMap> &Map, + FnTy &&Fn, ArgsTy&&... args) { + Optional &OptVal = Map[Key]; + if (!OptVal.hasValue()) + OptVal = Fn(std::forward(args)...); + return OptVal.getValue(); +} + +const BasicBlock * +MustBeExecutedContextExplorer::findForwardJoinPoint(const BasicBlock *InitBB) { + const LoopInfo *LI = LIGetter(*InitBB->getParent()); + const PostDominatorTree *PDT = PDTGetter(*InitBB->getParent()); + + LLVM_DEBUG(dbgs() << "\tFind forward join point for " << InitBB->getName() + << (LI ? " [LI]" : "") << (PDT ? " [PDT]" : "")); + + const Function &F = *InitBB->getParent(); + const Loop *L = LI ? LI->getLoopFor(InitBB) : nullptr; + const BasicBlock *HeaderBB = L ? L->getHeader() : InitBB; + bool WillReturnAndNoThrow = (F.hasFnAttribute(Attribute::WillReturn) || + (L && !maybeEndlessLoop(*L))) && + F.doesNotThrow(); + LLVM_DEBUG(dbgs() << (L ? " [in loop]" : "") + << (WillReturnAndNoThrow ? " [WillReturn] [NoUnwind]" : "") + << "\n"); + + // Determine the adjacent blocks in the given direction but exclude (self) + // loops under certain circumstances. + SmallVector Worklist; + for (const BasicBlock *SuccBB : successors(InitBB)) { + bool IsLatch = SuccBB == HeaderBB; + // Loop latches are ignored in forward propagation if the loop cannot be + // endless and may not throw: control has to go somewhere. + if (!WillReturnAndNoThrow || !IsLatch) + Worklist.push_back(SuccBB); + } + LLVM_DEBUG(dbgs() << "\t\t#Worklist: " << Worklist.size() << "\n"); + + // If there are no other adjacent blocks, there is no join point. + if (Worklist.empty()) + return nullptr; + + // If there is one adjacent block, it is the join point. + if (Worklist.size() == 1) + return Worklist[0]; + + // Try to determine a join block through the help of the post-dominance + // tree. If no tree was provided, we perform simple pattern matching for one + // block conditionals and one block loops only. + const BasicBlock *JoinBB = nullptr; + if (PDT) + if (const auto *InitNode = PDT->getNode(InitBB)) + if (const auto *IDomNode = InitNode->getIDom()) + JoinBB = IDomNode->getBlock(); + + if (!JoinBB && Worklist.size() == 2) { + const BasicBlock *Succ0 = Worklist[0]; + const BasicBlock *Succ1 = Worklist[1]; + const BasicBlock *Succ0UniqueSucc = Succ0->getUniqueSuccessor(); + const BasicBlock *Succ1UniqueSucc = Succ1->getUniqueSuccessor(); + if (Succ0UniqueSucc == InitBB) { + // InitBB -> Succ0 -> InitBB + // InitBB -> Succ1 = JoinBB + JoinBB = Succ1; + } else if (Succ1UniqueSucc == InitBB) { + // InitBB -> Succ1 -> InitBB + // InitBB -> Succ0 = JoinBB + JoinBB = Succ0; + } else if (Succ0 == Succ1UniqueSucc) { + // InitBB -> Succ0 = JoinBB + // InitBB -> Succ1 -> Succ0 = JoinBB + JoinBB = Succ0; + } else if (Succ1 == Succ0UniqueSucc) { + // InitBB -> Succ0 -> Succ1 = JoinBB + // InitBB -> Succ1 = JoinBB + JoinBB = Succ1; + } else if (Succ0UniqueSucc == Succ1UniqueSucc) { + // InitBB -> Succ0 -> JoinBB + // InitBB -> Succ1 -> JoinBB + JoinBB = Succ0UniqueSucc; + } + } + + if (!JoinBB && L) + JoinBB = L->getUniqueExitBlock(); + + if (!JoinBB) + return nullptr; + + LLVM_DEBUG(dbgs() << "\t\tJoin block candidate: " << JoinBB->getName() << "\n"); + + // In forward direction we check if control will for sure reach JoinBB from + // InitBB, thus it can not be "stopped" along the way. Ways to "stop" control + // are: infinite loops and instructions that do not necessarily transfer + // execution to their successor. To check for them we traverse the CFG from + // the adjacent blocks to the JoinBB, looking at all intermediate blocks. + + // If we know the function is "will-return" and "no-throw" there is no need + // for futher checks. + if (!F.hasFnAttribute(Attribute::WillReturn) || !F.doesNotThrow()) { + + auto BlockTransfersExecutionToSuccessor = [](const BasicBlock *BB) { + return isGuaranteedToTransferExecutionToSuccessor(BB); + }; + + SmallPtrSet Visited; + while (!Worklist.empty()) { + const BasicBlock *ToBB = Worklist.pop_back_val(); + if (ToBB == JoinBB) + continue; + + // Make sure all loops in-between are finite. + if (!Visited.insert(ToBB).second) { + if (!F.hasFnAttribute(Attribute::WillReturn)) { + if (!LI) + return nullptr; + + bool MayContainIrreducibleControl = getOrCreateCachedOptional( + &F, IrreducibleControlMap, mayContainIrreducibleControl, F, LI); + if (MayContainIrreducibleControl) + return nullptr; + + const Loop *L = LI->getLoopFor(ToBB); + if (L && maybeEndlessLoop(*L)) + return nullptr; + } + + continue; + } + + // Make sure the block has no instructions that could stop control + // transfer. + bool TransfersExecution = getOrCreateCachedOptional( + ToBB, BlockTransferMap, BlockTransfersExecutionToSuccessor, ToBB); + if (!TransfersExecution) + return nullptr; + + for (const BasicBlock *AdjacentBB : successors(ToBB)) + Worklist.push_back(AdjacentBB); + } + } + + LLVM_DEBUG(dbgs() << "\tJoin block: " << JoinBB->getName() << "\n"); + return JoinBB; +} + const Instruction * MustBeExecutedContextExplorer::getMustBeExecutedNextInstruction( MustBeExecutedIterator &It, const Instruction *PP) { @@ -490,6 +670,12 @@ return &PP->getSuccessor(0)->front(); } + // Multiple successors mean we need to find the join point where control flow + // converges again. We use the findForwardJoinPoint helper function with + // information about the function and helper analyses, if available. + if (const BasicBlock *JoinBB = findForwardJoinPoint(PP->getParent())) + return &JoinBB->front(); + LLVM_DEBUG(dbgs() << "\tNo join point found\n"); return nullptr; } diff --git a/llvm/test/Analysis/MustExecute/must_be_executed_context.ll b/llvm/test/Analysis/MustExecute/must_be_executed_context.ll --- a/llvm/test/Analysis/MustExecute/must_be_executed_context.ll +++ b/llvm/test/Analysis/MustExecute/must_be_executed_context.ll @@ -1,5 +1,6 @@ -; RUN: opt -print-mustexecute -analyze 2>&1 < %s | FileCheck %s --check-prefix=ME -; RUN: opt -print-must-be-executed-contexts -analyze 2>&1 < %s | FileCheck %s --check-prefix=MBEC +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -print-mustexecute -analyze 2>&1 | FileCheck %s --check-prefix=ME +; RUN: opt < %s -print-must-be-executed-contexts -analyze 2>&1 | FileCheck %s --check-prefix=MBEC ; ; void simple_conditional(int c) { ; A(); @@ -36,6 +37,8 @@ ; MBEC-NEXT: [F: simple_conditional] call void @B() ; MBEC-NEXT: [F: simple_conditional] %tmp = icmp eq i32 %arg, 0 ; MBEC-NEXT: [F: simple_conditional] br i1 %tmp, label %bb2, label %bb1 +; MBEC-NEXT: [F: simple_conditional] call void @E() +; MBEC-NEXT: [F: simple_conditional] call void @F() ; MBEC-NOT: call call void @B() @@ -43,6 +46,8 @@ ; MBEC-NEXT: [F: simple_conditional] call void @B() ; MBEC-NEXT: [F: simple_conditional] %tmp = icmp eq i32 %arg, 0 ; MBEC-NEXT: [F: simple_conditional] br i1 %tmp, label %bb2, label %bb1 +; MBEC-NEXT: [F: simple_conditional] call void @E() +; MBEC-NEXT: [F: simple_conditional] call void @F() ; MBEC-NOT: call ; MBEC: -- Explore context of: %tmp @@ -280,3 +285,115 @@ declare void @F() nounwind declare void @G() nounwind willreturn + +declare i32 @g(i32*) nounwind willreturn + +declare void @h(i32*) nounwind willreturn + +define i32 @nonnull_exec_ctx_1(i32* %a, i32 %b) { +; MBEC: -- Explore context of: %tmp3 = icmp eq i32 %b, 0 +; MBEC-NEXT: [F: nonnull_exec_ctx_1] %tmp3 = icmp eq i32 %b, 0 +; MBEC-NEXT: [F: nonnull_exec_ctx_1] br i1 %tmp3, label %ex, label %hd +; MBEC-NEXT: -- Explore context of: br i1 %tmp3, label %ex, label %hd +; MBEC-NEXT: [F: nonnull_exec_ctx_1] br i1 %tmp3, label %ex, label %hd +; MBEC-NEXT: -- Explore context of: %tmp5 = tail call i32 @g(i32* nonnull %a) +; MBEC-NEXT: [F: nonnull_exec_ctx_1] %tmp5 = tail call i32 @g(i32* nonnull %a) +; MBEC-NEXT: [F: nonnull_exec_ctx_1] ret i32 %tmp5 +; MBEC-NEXT: -- Explore context of: ret i32 %tmp5 +; MBEC-NEXT: [F: nonnull_exec_ctx_1] ret i32 %tmp5 +; MBEC-NEXT: -- Explore context of: %tmp7 = phi i32 [ %tmp8, %hd ], [ 0, %en ] +; MBEC-NEXT: [F: nonnull_exec_ctx_1] %tmp7 = phi i32 [ %tmp8, %hd ], [ 0, %en ] +; MBEC-NEXT: [F: nonnull_exec_ctx_1] tail call void @h(i32* %a) +; MBEC-NEXT: [F: nonnull_exec_ctx_1] %tmp8 = add nuw i32 %tmp7, 1 +; MBEC-NEXT: [F: nonnull_exec_ctx_1] %tmp9 = icmp eq i32 %tmp8, %b +; MBEC-NEXT: [F: nonnull_exec_ctx_1] br i1 %tmp9, label %ex, label %hd +; MBEC-NEXT: -- Explore context of: tail call void @h(i32* %a) +; MBEC-NEXT: [F: nonnull_exec_ctx_1] tail call void @h(i32* %a) +; MBEC-NEXT: [F: nonnull_exec_ctx_1] %tmp8 = add nuw i32 %tmp7, 1 +; MBEC-NEXT: [F: nonnull_exec_ctx_1] %tmp9 = icmp eq i32 %tmp8, %b +; MBEC-NEXT: [F: nonnull_exec_ctx_1] br i1 %tmp9, label %ex, label %hd +; MBEC-NEXT: -- Explore context of: %tmp8 = add nuw i32 %tmp7, 1 +; MBEC-NEXT: [F: nonnull_exec_ctx_1] %tmp8 = add nuw i32 %tmp7, 1 +; MBEC-NEXT: [F: nonnull_exec_ctx_1] %tmp9 = icmp eq i32 %tmp8, %b +; MBEC-NEXT: [F: nonnull_exec_ctx_1] br i1 %tmp9, label %ex, label %hd +; MBEC-NEXT: -- Explore context of: %tmp9 = icmp eq i32 %tmp8, %b +; MBEC-NEXT: [F: nonnull_exec_ctx_1] %tmp9 = icmp eq i32 %tmp8, %b +; MBEC-NEXT: [F: nonnull_exec_ctx_1] br i1 %tmp9, label %ex, label %hd +; MBEC-NEXT: -- Explore context of: br i1 %tmp9, label %ex, label %hd +; MBEC-NEXT: [F: nonnull_exec_ctx_1] br i1 %tmp9, label %ex, label %hd +en: + %tmp3 = icmp eq i32 %b, 0 + br i1 %tmp3, label %ex, label %hd + +ex: + %tmp5 = tail call i32 @g(i32* nonnull %a) + ret i32 %tmp5 + +hd: + %tmp7 = phi i32 [ %tmp8, %hd ], [ 0, %en ] + tail call void @h(i32* %a) + %tmp8 = add nuw i32 %tmp7, 1 + %tmp9 = icmp eq i32 %tmp8, %b + br i1 %tmp9, label %ex, label %hd +} + +define i32 @nonnull_exec_ctx_2(i32* %a, i32 %b) nounwind willreturn { +; MBEC: -- Explore context of: %tmp3 = icmp eq i32 %b, 0 +; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp3 = icmp eq i32 %b, 0 +; MBEC-NEXT: [F: nonnull_exec_ctx_2] br i1 %tmp3, label %ex, label %hd +; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp5 = tail call i32 @g(i32* nonnull %a) +; MBEC-NEXT: [F: nonnull_exec_ctx_2] ret i32 %tmp5 +; MBEC-NEXT: -- Explore context of: br i1 %tmp3, label %ex, label %hd +; MBEC-NEXT: [F: nonnull_exec_ctx_2] br i1 %tmp3, label %ex, label %hd +; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp5 = tail call i32 @g(i32* nonnull %a) +; MBEC-NEXT: [F: nonnull_exec_ctx_2] ret i32 %tmp5 +; MBEC-NEXT: -- Explore context of: %tmp5 = tail call i32 @g(i32* nonnull %a) +; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp5 = tail call i32 @g(i32* nonnull %a) +; MBEC-NEXT: [F: nonnull_exec_ctx_2] ret i32 %tmp5 +; MBEC-NEXT: -- Explore context of: ret i32 %tmp5 +; MBEC-NEXT: [F: nonnull_exec_ctx_2] ret i32 %tmp5 +; MBEC-NEXT: -- Explore context of: %tmp7 = phi i32 [ %tmp8, %hd ], [ 0, %en ] +; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp7 = phi i32 [ %tmp8, %hd ], [ 0, %en ] +; MBEC-NEXT: [F: nonnull_exec_ctx_2] tail call void @h(i32* %a) +; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp8 = add nuw i32 %tmp7, 1 +; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp9 = icmp eq i32 %tmp8, %b +; MBEC-NEXT: [F: nonnull_exec_ctx_2] br i1 %tmp9, label %ex, label %hd +; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp5 = tail call i32 @g(i32* nonnull %a) +; MBEC-NEXT: [F: nonnull_exec_ctx_2] ret i32 %tmp5 +; MBEC-NEXT: -- Explore context of: tail call void @h(i32* %a) +; MBEC-NEXT: [F: nonnull_exec_ctx_2] tail call void @h(i32* %a) +; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp8 = add nuw i32 %tmp7, 1 +; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp9 = icmp eq i32 %tmp8, %b +; MBEC-NEXT: [F: nonnull_exec_ctx_2] br i1 %tmp9, label %ex, label %hd +; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp5 = tail call i32 @g(i32* nonnull %a) +; MBEC-NEXT: [F: nonnull_exec_ctx_2] ret i32 %tmp5 +; MBEC-NEXT: -- Explore context of: %tmp8 = add nuw i32 %tmp7, 1 +; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp8 = add nuw i32 %tmp7, 1 +; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp9 = icmp eq i32 %tmp8, %b +; MBEC-NEXT: [F: nonnull_exec_ctx_2] br i1 %tmp9, label %ex, label %hd +; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp5 = tail call i32 @g(i32* nonnull %a) +; MBEC-NEXT: [F: nonnull_exec_ctx_2] ret i32 %tmp5 +; MBEC-NEXT: -- Explore context of: %tmp9 = icmp eq i32 %tmp8, %b +; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp9 = icmp eq i32 %tmp8, %b +; MBEC-NEXT: [F: nonnull_exec_ctx_2] br i1 %tmp9, label %ex, label %hd +; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp5 = tail call i32 @g(i32* nonnull %a) +; MBEC-NEXT: [F: nonnull_exec_ctx_2] ret i32 %tmp5 +; MBEC-NEXT: -- Explore context of: br i1 %tmp9, label %ex, label %hd +; MBEC-NEXT: [F: nonnull_exec_ctx_2] br i1 %tmp9, label %ex, label %hd +; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp5 = tail call i32 @g(i32* nonnull %a) +; MBEC-NEXT: [F: nonnull_exec_ctx_2] ret i32 %tmp5 +en: + %tmp3 = icmp eq i32 %b, 0 + br i1 %tmp3, label %ex, label %hd + +ex: + %tmp5 = tail call i32 @g(i32* nonnull %a) + ret i32 %tmp5 + +hd: + %tmp7 = phi i32 [ %tmp8, %hd ], [ 0, %en ] + tail call void @h(i32* %a) + %tmp8 = add nuw i32 %tmp7, 1 + %tmp9 = icmp eq i32 %tmp8, %b + br i1 %tmp9, label %ex, label %hd +} diff --git a/llvm/test/Transforms/FunctionAttrs/nonnull.ll b/llvm/test/Transforms/FunctionAttrs/nonnull.ll --- a/llvm/test/Transforms/FunctionAttrs/nonnull.ll +++ b/llvm/test/Transforms/FunctionAttrs/nonnull.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -S -functionattrs -enable-nonnull-arg-prop %s | FileCheck %s --check-prefixes=BOTH,FNATTR ; RUN: opt -S -passes=function-attrs -enable-nonnull-arg-prop %s | FileCheck %s --check-prefixes=BOTH,FNATTR ; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=8 -S < %s | FileCheck %s --check-prefixes=BOTH,ATTRIBUTOR @@ -159,7 +160,7 @@ ret void } define internal void @test13(i8* %a, i8* %b, i8* %c) { -; ATTRIBUTOR: define internal void @test13(i8* nocapture nonnull readnone %a, i8* nocapture readnone %b, i8* nocapture readnone %c) +; ATTRIBUTOR: define internal void @test13(i8* nocapture nonnull readnone %a, i8* nocapture readnone %b, i8* nocapture readnone %c) ret void } @@ -172,7 +173,7 @@ ; * Argument ; 1. In f1:bb6, %arg can be marked with nonnull because of the comparison in bb1 ; 2. Because f2 is internal function, f2(i32* %arg) -> @f2(i32* nonnull %arg) -; 3. In f1:bb4 %tmp5 is nonnull and f3 is internal function. +; 3. In f1:bb4 %tmp5 is nonnull and f3 is internal function. ; Then, f3(i32* %arg) -> @f3(i32* nonnull %arg) ; 4. We get nonnull in whole f1 call sites so f1(i32* %arg) -> @f1(i32* nonnull %arg) @@ -208,21 +209,21 @@ } define internal i32* @f2(i32* %arg) { -; FIXME: missing nonnull. It should be nonnull @f2(i32* nonnull %arg) +; FIXME: missing nonnull. It should be nonnull @f2(i32* nonnull %arg) ; ATTRIBUTOR: define internal nonnull i32* @f2(i32* readonly %arg) bb: -; FIXME: missing nonnull. It should be @f1(i32* nonnull readonly %arg) +; FIXME: missing nonnull. It should be @f1(i32* nonnull readonly %arg) ; ATTRIBUTOR: %tmp = tail call nonnull i32* @f1(i32* readonly %arg) %tmp = tail call i32* @f1(i32* %arg) ret i32* %tmp } define dso_local noalias i32* @f3(i32* %arg) { -; FIXME: missing nonnull. It should be nonnull @f3(i32* nonnull readonly %arg) +; FIXME: missing nonnull. It should be nonnull @f3(i32* nonnull readonly %arg) ; ATTRIBUTOR: define dso_local noalias i32* @f3(i32* nocapture readonly %arg) bb: -; FIXME: missing nonnull. It should be @f1(i32* nonnull readonly %arg) +; FIXME: missing nonnull. It should be @f1(i32* nonnull readonly %arg) ; ATTRIBUTOR: %tmp = call i32* @f1(i32* readonly %arg) %tmp = call i32* @f1(i32* %arg) ret i32* null @@ -266,8 +267,7 @@ ; 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) +; ATTRIBUTOR: define void @f17(i8* nonnull %a, i8 %c) %cmp = icmp eq i8 %c, 0 br i1 %cmp, label %if.then, label %if.else if.then: @@ -292,8 +292,7 @@ ; 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) +; ATTRIBUTOR: define void @f18(i8* nonnull %a, i8* %b, i8 %c) %cmp1 = icmp eq i8 %c, 0 br i1 %cmp1, label %if.then, label %if.else if.then: @@ -477,7 +476,7 @@ declare i32 @esfp(...) define i1 @parent8(i8* %a, i8* %bogus1, i8* %b) personality i8* bitcast (i32 (...)* @esfp to i8*){ -; BOTH-LABEL: @parent8(i8* nonnull %a, i8* nocapture readnone %bogus1, i8* nonnull %b) +; BOTH-LABEL: @parent8(i8* nonnull %a, i8* nocapture readnone %bogus1, i8* nonnull %b) ; BOTH-NEXT: entry: ; FNATTR-NEXT: invoke void @use2nonnull(i8* %a, i8* %b) ; ATTRIBUTOR-NEXT: invoke void @use2nonnull(i8* nonnull %a, i8* nonnull %b) @@ -579,5 +578,216 @@ ret void } + +;int f(int *u, int n){ +; for(int i = 0;i