[llvm] fe799c9 - [MustExecute] Forward iterate over conditional branches

Mikael Holmén via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 31 01:51:10 PDT 2019


Hi Johannes,

I fixed a clang warning introduced with this patch in c95049540, please
take a look and check that I didn't mess up.

Thanks,
Mikael

On Wed, 2019-10-30 at 22:07 -0700, Johannes Doerfert via llvm-commits
wrote:
> Author: Johannes Doerfert
> Date: 2019-10-31T00:06:43-05:00
> New Revision: fe799c97fae0729e5952c6a8edf41e67bf60048f
> 
> URL: 
> https://protect2.fireeye.com/v1/url?k=183fbbd7-44adb6c8-183ffb4c-0cc47ad93db4-fbb129546cbfefd4&q=1&e=c8a13624-45f8-40b4-b6b1-85eaf8c5ce1d&u=https%3A%2F%2Fgithub.com%2Fllvm%2Fllvm-project%2Fcommit%2Ffe799c97fae0729e5952c6a8edf41e67bf60048f
> DIFF: 
> https://protect2.fireeye.com/v1/url?k=a1183625-fd8a3b3a-a11876be-0cc47ad93db4-b131f81bce55dfa9&q=1&e=c8a13624-45f8-40b4-b6b1-85eaf8c5ce1d&u=https%3A%2F%2Fgithub.com%2Fllvm%2Fllvm-project%2Fcommit%2Ffe799c97fae0729e5952c6a8edf41e67bf60048f.diff
> 
> LOG: [MustExecute] Forward iterate over conditional branches
> 
> Summary:
> If a conditional branch is encountered we can try to find a join
> block
> where the execution is known to continue. This means finding a
> suitable
> block, e.g., the immediate post dominator of the conditional branch,
> and
> proofing control will always reach that block.
> 
> This patch implements different techniques that work with and without
> provided analysis.
> 
> Reviewers: uenoku, sstefan1, hfinkel
> 
> Subscribers: hiraditya, bollu, llvm-commits
> 
> Tags: #llvm
> 
> Differential Revision: 
> https://protect2.fireeye.com/v1/url?k=13c95c03-4f5b511c-13c91c98-0cc47ad93db4-673e381fd96836a0&q=1&e=c8a13624-45f8-40b4-b6b1-85eaf8c5ce1d&u=https%3A%2F%2Freviews.llvm.org%2FD68933
> 
> Added: 
>     
> 
> Modified: 
>     llvm/include/llvm/Analysis/MustExecute.h
>     llvm/lib/Analysis/MustExecute.cpp
>     llvm/test/Analysis/MustExecute/must_be_executed_context.ll
>     llvm/test/Transforms/FunctionAttrs/nonnull.ll
> 
> Removed: 
>     
> 
> 
> #####################################################################
> ###########
> diff  --git a/llvm/include/llvm/Analysis/MustExecute.h
> b/llvm/include/llvm/Analysis/MustExecute.h
> index 045171b706f1..d88cdf40ed87 100644
> --- a/llvm/include/llvm/Analysis/MustExecute.h
> +++ b/llvm/include/llvm/Analysis/MustExecute.h
> @@ -33,8 +33,13 @@
>  
>  namespace llvm {
>  
> +namespace {
> +template <typename T> using GetterTy = std::function<T *(const
> Function &F)>;
> +}
> +
>  class Instruction;
>  class DominatorTree;
> +class PostDominatorTree;
>  class Loop;
>  
>  /// Captures loop safety information.
> @@ -374,8 +379,14 @@ struct MustBeExecutedContextExplorer {
>    /// \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<const LoopInfo> LIGetter =
> +          [](const Function &) { return nullptr; },
> +      GetterTy<const PostDominatorTree> 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 @@ struct MustBeExecutedContextExplorer {
>    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 @@ struct MustBeExecutedContextExplorer {
>    ///}
>  
>  private:
> +  /// Getters for common CFG analyses: LoopInfo, DominatorTree, and
> +  /// PostDominatorTree.
> +  ///{
> +  GetterTy<const LoopInfo> LIGetter;
> +  GetterTy<const PostDominatorTree> PDTGetter;
> +  ///}
> +
> +  /// Map to cache isGuaranteedToTransferExecutionToSuccessor
> results.
> +  DenseMap<const BasicBlock *, Optional<bool>> BlockTransferMap;
> +
> +  /// Map to cache containsIrreducibleCFG results.
> +  DenseMap<const Function*, Optional<bool>> IrreducibleControlMap;
> +
>    /// Map from instructions to associated must be executed
> iterators.
>    DenseMap<const Instruction *, MustBeExecutedIterator *>
>        InstructionIteratorMap;
> 
> diff  --git a/llvm/lib/Analysis/MustExecute.cpp
> b/llvm/lib/Analysis/MustExecute.cpp
> index 44527773115d..a796cc79ad89 100644
> --- 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 @@ ModulePass
> *llvm::createMustBeExecutedContextPrinter() {
>  }
>  
>  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<LoopInfo> LIGetter = [this](const Function &F) {
> +    DominatorTree *DT = new DominatorTree(const_cast<Function
> &>(F));
> +    LoopInfo *LI = new LoopInfo(*DT);
> +    return LI;
> +  };
> +  GetterTy<PostDominatorTree> PDTGetter = [this](const Function &F)
> {
> +    PostDominatorTree *PDT = new
> PostDominatorTree(const_cast<Function &>(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 @@ bool MustExecutePrinter::runOnFunction(Function
> &F) {
>    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<const Function *>;
> +  RPOTraversal FuncRPOT(&F);
> +  return !containsIrreducibleCFG<const BasicBlock *, const
> RPOTraversal,
> +                                 const LoopInfo>(FuncRPOT, *LI);
> +}
> +
> +/// Lookup \p Key in \p Map and return the result, potentially after
> +/// initializing the optional through \p Fn(\p args).
> +template <typename K, typename V, typename FnTy, typename... ArgsTy>
> +static V getOrCreateCachedOptional(K Key, DenseMap<K, Optional<V>>
> &Map,
> +                                   FnTy &&Fn, ArgsTy&&... args) {
> +  Optional<V> &OptVal = Map[Key];
> +  if (!OptVal.hasValue())
> +    OptVal = Fn(std::forward<ArgsTy>(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<const BasicBlock *, 8> 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<const BasicBlock *, 16> 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 @@
> MustBeExecutedContextExplorer::getMustBeExecutedNextInstruction(
>      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
> index da1aa7280b02..fd650872dd91 100644
> --- 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 @@ bb:
>  ; 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 @@ bb:
>  ; 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 @E() nounwind willreturn
>  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
> index 9a7eb114eaee..657ef7152eb7 100644
> --- 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 @@ define void @test13_helper() {
>    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 @@ declare nonnull i8* @nonnull()
>  ; * 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 @@
> bb9:                                              ; preds = %bb4, %bb
>  }
>  
>  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 @@ if.else:
>  ; 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 @@ cont:
>  ; 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 @@ define i8 @parent7(i8* %a) {
>  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 @@ define void @make_live(i32* nonnull
> dereferenceable(8) %a) {
>    ret void
>  }
>  
> +
> +;int f(int *u, int n){
> +;  for(int i = 0;i<n;i++){
> +;    h(u);
> +;  }
> +;  return g(nonnull u);
> +;}
> +declare void @h(i32*) willreturn nounwind
> +declare i32 @g(i32*) willreturn nounwind
> +define i32 @nonnull_exec_ctx_1(i32* %a, i32 %b) {
> +; FNATTR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_1
> +; FNATTR-SAME: (i32* [[A:%.*]], i32 [[B:%.*]]) #3
> +; FNATTR-NEXT:  en:
> +; FNATTR-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
> +; FNATTR-NEXT:    br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
> +; FNATTR:       ex:
> +; FNATTR-NEXT:    [[TMP5:%.*]] = tail call i32 @g(i32* nonnull
> [[A:%.*]])
> +; FNATTR-NEXT:    ret i32 [[TMP5]]
> +; FNATTR:       hd:
> +; FNATTR-NEXT:    [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [
> 0, [[EN:%.*]] ]
> +; FNATTR-NEXT:    tail call void @h(i32* [[A]])
> +; FNATTR-NEXT:    [[TMP8]] = add nuw i32 [[TMP7]], 1
> +; FNATTR-NEXT:    [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
> +; FNATTR-NEXT:    br i1 [[TMP9]], label [[EX]], label [[HD]]
> +;
> +; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_1
> +; ATTRIBUTOR-SAME: (i32* [[A:%.*]], i32 [[B:%.*]]) #5
> +; ATTRIBUTOR-NEXT:  en:
> +; ATTRIBUTOR-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
> +; ATTRIBUTOR-NEXT:    br i1 [[TMP3]], label [[EX:%.*]], label
> [[HD:%.*]]
> +; ATTRIBUTOR:       ex:
> +; ATTRIBUTOR-NEXT:    [[TMP5:%.*]] = tail call i32 @g(i32* nonnull
> [[A:%.*]])
> +; ATTRIBUTOR-NEXT:    ret i32 [[TMP5]]
> +; ATTRIBUTOR:       hd:
> +; ATTRIBUTOR-NEXT:    [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]]
> ], [ 0, [[EN:%.*]] ]
> +; ATTRIBUTOR-NEXT:    tail call void @h(i32* [[A]])
> +; ATTRIBUTOR-NEXT:    [[TMP8]] = add nuw i32 [[TMP7]], 1
> +; ATTRIBUTOR-NEXT:    [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
> +; ATTRIBUTOR-NEXT:    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_1b(i32* %a, i32 %b) {
> +; FNATTR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_1b
> +; FNATTR-SAME: (i32* [[A:%.*]], i32 [[B:%.*]]) #3
> +; FNATTR-NEXT:  en:
> +; FNATTR-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
> +; FNATTR-NEXT:    br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
> +; FNATTR:       ex:
> +; FNATTR-NEXT:    [[TMP5:%.*]] = tail call i32 @g(i32* nonnull
> [[A:%.*]])
> +; FNATTR-NEXT:    ret i32 [[TMP5]]
> +; FNATTR:       hd:
> +; FNATTR-NEXT:    [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]]
> ], [ 0, [[EN:%.*]] ]
> +; FNATTR-NEXT:    tail call void @h(i32* [[A]])
> +; FNATTR-NEXT:    br label [[HD2]]
> +; FNATTR:       hd2:
> +; FNATTR-NEXT:    [[TMP8]] = add nuw i32 [[TMP7]], 1
> +; FNATTR-NEXT:    [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
> +; FNATTR-NEXT:    br i1 [[TMP9]], label [[EX]], label [[HD]]
> +;
> +; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_1b
> +; ATTRIBUTOR-SAME: (i32* [[A:%.*]], i32 [[B:%.*]]) #5
> +; ATTRIBUTOR-NEXT:  en:
> +; ATTRIBUTOR-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
> +; ATTRIBUTOR-NEXT:    br i1 [[TMP3]], label [[EX:%.*]], label
> [[HD:%.*]]
> +; ATTRIBUTOR:       ex:
> +; ATTRIBUTOR-NEXT:    [[TMP5:%.*]] = tail call i32 @g(i32* nonnull
> [[A:%.*]])
> +; ATTRIBUTOR-NEXT:    ret i32 [[TMP5]]
> +; ATTRIBUTOR:       hd:
> +; ATTRIBUTOR-NEXT:    [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]],
> [[HD2:%.*]] ], [ 0, [[EN:%.*]] ]
> +; ATTRIBUTOR-NEXT:    tail call void @h(i32* [[A]])
> +; ATTRIBUTOR-NEXT:    br label [[HD2]]
> +; ATTRIBUTOR:       hd2:
> +; ATTRIBUTOR-NEXT:    [[TMP8]] = add nuw i32 [[TMP7]], 1
> +; ATTRIBUTOR-NEXT:    [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
> +; ATTRIBUTOR-NEXT:    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, %hd2 ], [ 0, %en ]
> +  tail call void @h(i32* %a)
> +  br label %hd2
> +
> +hd2:
> +  %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) willreturn nounwind
> {
> +; FNATTR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_2
> +; FNATTR-SAME: (i32* [[A:%.*]], i32 [[B:%.*]]) #2
> +; FNATTR-NEXT:  en:
> +; FNATTR-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
> +; FNATTR-NEXT:    br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
> +; FNATTR:       ex:
> +; FNATTR-NEXT:    [[TMP5:%.*]] = tail call i32 @g(i32* nonnull
> [[A:%.*]])
> +; FNATTR-NEXT:    ret i32 [[TMP5]]
> +; FNATTR:       hd:
> +; FNATTR-NEXT:    [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [
> 0, [[EN:%.*]] ]
> +; FNATTR-NEXT:    tail call void @h(i32* [[A]])
> +; FNATTR-NEXT:    [[TMP8]] = add nuw i32 [[TMP7]], 1
> +; FNATTR-NEXT:    [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
> +; FNATTR-NEXT:    br i1 [[TMP9]], label [[EX]], label [[HD]]
> +;
> +; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_2
> +; ATTRIBUTOR-SAME: (i32* [[A:%.*]], i32 [[B:%.*]]) #3
> +; ATTRIBUTOR-NEXT:  en:
> +; ATTRIBUTOR-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
> +; ATTRIBUTOR-NEXT:    br i1 [[TMP3]], label [[EX:%.*]], label
> [[HD:%.*]]
> +; ATTRIBUTOR:       ex:
> +; ATTRIBUTOR-NEXT:    [[TMP5:%.*]] = tail call i32 @g(i32* nonnull
> [[A:%.*]])
> +; ATTRIBUTOR-NEXT:    ret i32 [[TMP5]]
> +; ATTRIBUTOR:       hd:
> +; ATTRIBUTOR-NEXT:    [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]]
> ], [ 0, [[EN:%.*]] ]
> +; ATTRIBUTOR-NEXT:    tail call void @h(i32* nonnull [[A]])
> +; ATTRIBUTOR-NEXT:    [[TMP8]] = add nuw i32 [[TMP7]], 1
> +; ATTRIBUTOR-NEXT:    [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
> +; ATTRIBUTOR-NEXT:    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_2b(i32* %a, i32 %b) willreturn nounwind
> {
> +; FNATTR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_2b
> +; FNATTR-SAME: (i32* [[A:%.*]], i32 [[B:%.*]]) #2
> +; FNATTR-NEXT:  en:
> +; FNATTR-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
> +; FNATTR-NEXT:    br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
> +; FNATTR:       ex:
> +; FNATTR-NEXT:    [[TMP5:%.*]] = tail call i32 @g(i32* nonnull
> [[A:%.*]])
> +; FNATTR-NEXT:    ret i32 [[TMP5]]
> +; FNATTR:       hd:
> +; FNATTR-NEXT:    [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]]
> ], [ 0, [[EN:%.*]] ]
> +; FNATTR-NEXT:    tail call void @h(i32* [[A]])
> +; FNATTR-NEXT:    br label [[HD2]]
> +; FNATTR:       hd2:
> +; FNATTR-NEXT:    [[TMP8]] = add nuw i32 [[TMP7]], 1
> +; FNATTR-NEXT:    [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
> +; FNATTR-NEXT:    br i1 [[TMP9]], label [[EX]], label [[HD]]
> +;
> +; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_2b
> +; ATTRIBUTOR-SAME: (i32* [[A:%.*]], i32 [[B:%.*]]) #3
> +; ATTRIBUTOR-NEXT:  en:
> +; ATTRIBUTOR-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
> +; ATTRIBUTOR-NEXT:    br i1 [[TMP3]], label [[EX:%.*]], label
> [[HD:%.*]]
> +; ATTRIBUTOR:       ex:
> +; ATTRIBUTOR-NEXT:    [[TMP5:%.*]] = tail call i32 @g(i32* nonnull
> [[A:%.*]])
> +; ATTRIBUTOR-NEXT:    ret i32 [[TMP5]]
> +; ATTRIBUTOR:       hd:
> +; ATTRIBUTOR-NEXT:    [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]],
> [[HD2:%.*]] ], [ 0, [[EN:%.*]] ]
> +; ATTRIBUTOR-NEXT:    tail call void @h(i32* nonnull [[A]])
> +; ATTRIBUTOR-NEXT:    br label [[HD2]]
> +; ATTRIBUTOR:       hd2:
> +; ATTRIBUTOR-NEXT:    [[TMP8]] = add nuw i32 [[TMP7]], 1
> +; ATTRIBUTOR-NEXT:    [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
> +; ATTRIBUTOR-NEXT:    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, %hd2 ], [ 0, %en ]
> +  tail call void @h(i32* %a)
> +  br label %hd2
> +
> +hd2:
> +  %tmp8 = add nuw i32 %tmp7, 1
> +  %tmp9 = icmp eq i32 %tmp8, %b
> +  br i1 %tmp9, label %ex, label %hd
> +}
> +
>  attributes #0 = { "null-pointer-is-valid"="true" }
>  attributes #1 = { nounwind willreturn}
> 
> 
>         
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at lists.llvm.org
> 
https://protect2.fireeye.com/v1/url?k=afe4c3ec-f376cef3-afe48377-0cc47ad93db4-60833c49f3d9d376&q=1&e=c8a13624-45f8-40b4-b6b1-85eaf8c5ce1d&u=https%3A%2F%2Flists.llvm.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fllvm-commits


More information about the llvm-commits mailing list