<div dir="ltr"><div dir="ltr"><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, Jul 22, 2019 at 4:57 PM Stefan Stipanovic via llvm-commits <<a href="mailto:llvm-commits@lists.llvm.org">llvm-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Author: sstefan<br>
Date: Mon Jul 22 16:58:23 2019<br>
New Revision: 366769<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=366769&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=366769&view=rev</a><br>
Log:<br>
Fixing build error from commit 95cbc3d<br>
<br>
[Attributor] Liveness analysis.<br>
<br>
Liveness analysis abstract attribute used to indicate which BasicBlocks are dead and can therefore be ignored.<br>
Right now we are only looking at noreturn calls.<br>
<br>
Reviewers: jdoerfert, uenoku<br>
<br>
Subscribers: hiraditya, llvm-commits<br>
<br>
Differential Revision: <a href="https://reviews.llvm.org/D64162" rel="noreferrer" target="_blank">https://reviews.llvm.org/D64162</a><br>
<br>
Added:<br>
    llvm/trunk/test/Transforms/FunctionAttrs/liveness.ll<br>
Modified:<br>
    llvm/trunk/include/llvm/Transforms/IPO/Attributor.h<br>
    llvm/trunk/include/llvm/Transforms/Utils/Local.h<br>
    llvm/trunk/lib/Transforms/IPO/Attributor.cpp<br>
    llvm/trunk/lib/Transforms/Utils/Local.cpp<br>
<br>
Modified: llvm/trunk/include/llvm/Transforms/IPO/Attributor.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Transforms/IPO/Attributor.h?rev=366769&r1=366768&r2=366769&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Transforms/IPO/Attributor.h?rev=366769&r1=366768&r2=366769&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/include/llvm/Transforms/IPO/Attributor.h (original)<br>
+++ llvm/trunk/include/llvm/Transforms/IPO/Attributor.h Mon Jul 22 16:58:23 2019<br>
@@ -805,6 +805,46 @@ struct AANoAlias : public AbstractAttrib<br>
   static constexpr Attribute::AttrKind ID = Attribute::NoAlias;<br>
 };<br>
<br>
+/// An AbstractAttribute for noreturn.<br>
+struct AANoReturn : public AbstractAttribute {<br>
+<br>
+  /// See AbstractAttribute::AbstractAttribute(...).<br>
+  AANoReturn(Value &V, InformationCache &InfoCache)<br>
+      : AbstractAttribute(V, InfoCache) {}<br>
+<br>
+  /// Return true if the underlying object is known to never return.<br>
+  virtual bool isKnownNoReturn() const = 0;<br>
+<br>
+  /// Return true if the underlying object is assumed to never return.<br>
+  virtual bool isAssumedNoReturn() const = 0;<br>
+<br>
+  /// See AbstractAttribute::getAttrKind()<br>
+  Attribute::AttrKind getAttrKind() const override { return ID; }<br>
+<br>
+  /// The identifier used by the Attributor for this class of attributes.<br>
+  static constexpr Attribute::AttrKind ID = Attribute::NoReturn;<br>
+};<br>
+<br>
+/// An abstract interface for liveness abstract attribute.<br>
+struct AAIsDead : public AbstractAttribute {<br>
+<br>
+  /// See AbstractAttribute::AbstractAttribute(...).<br>
+  AAIsDead(Value &V, InformationCache &InfoCache)<br>
+      : AbstractAttribute(V, InfoCache) {}<br>
+<br>
+  /// See AbstractAttribute::getAttrKind()<br>
+  Attribute::AttrKind getAttrKind() const override { return ID; }<br>
+<br>
+  static constexpr Attribute::AttrKind ID =<br>
+      Attribute::AttrKind(Attribute::EndAttrKinds + 1);<br>
+<br>
+  /// Returns true if \p BB is assumed dead.<br>
+  virtual bool isAssumedDead(BasicBlock *BB) const = 0;<br>
+<br>
+  /// Returns true if \p BB is known dead.<br>
+  virtual bool isKnownDead(BasicBlock *BB) const = 0;<br>
+};<br>
+<br>
 } // end namespace llvm<br>
<br>
 #endif // LLVM_TRANSFORMS_IPO_FUNCTIONATTRS_H<br>
<br>
Modified: llvm/trunk/include/llvm/Transforms/Utils/Local.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Transforms/Utils/Local.h?rev=366769&r1=366768&r2=366769&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Transforms/Utils/Local.h?rev=366769&r1=366768&r2=366769&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/include/llvm/Transforms/Utils/Local.h (original)<br>
+++ llvm/trunk/include/llvm/Transforms/Utils/Local.h Mon Jul 22 16:58:23 2019<br>
@@ -271,6 +271,9 @@ inline unsigned getKnownAlignment(Value<br>
   return getOrEnforceKnownAlignment(V, 0, DL, CxtI, AC, DT);<br>
 }<br>
<br>
+/// This function converts the specified invoek into a normall call.<br>
+void changeToCall(InvokeInst *II, DomTreeUpdater *DTU = nullptr);<br>
+<br>
 ///===---------------------------------------------------------------------===//<br>
 ///  Dbg Intrinsic utilities<br>
 ///<br>
<br>
Modified: llvm/trunk/lib/Transforms/IPO/Attributor.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/Attributor.cpp?rev=366769&r1=366768&r2=366769&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/Attributor.cpp?rev=366769&r1=366768&r2=366769&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/Transforms/IPO/Attributor.cpp (original)<br>
+++ llvm/trunk/lib/Transforms/IPO/Attributor.cpp Mon Jul 22 16:58:23 2019<br>
@@ -16,6 +16,7 @@<br>
 #include "llvm/Transforms/IPO/Attributor.h"<br>
<br>
 #include "llvm/ADT/DepthFirstIterator.h"<br>
+#include "llvm/ADT/STLExtras.h"<br>
 #include "llvm/ADT/SetVector.h"<br>
 #include "llvm/ADT/SmallPtrSet.h"<br>
 #include "llvm/ADT/SmallVector.h"<br>
@@ -31,6 +32,9 @@<br>
 #include "llvm/Support/CommandLine.h"<br>
 #include "llvm/Support/Debug.h"<br>
 #include "llvm/Support/raw_ostream.h"<br>
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"<br>
+#include "llvm/Transforms/Utils/Local.h"<br>
+<br>
 #include <cassert><br>
<br>
 using namespace llvm;<br>
@@ -1411,6 +1415,173 @@ ChangeStatus AANoAliasReturned::updateIm<br>
   return ChangeStatus::UNCHANGED;<br>
 }<br>
<br>
+/// -------------------AAIsDead Function Attribute-----------------------<br>
+<br>
+struct AAIsDeadFunction : AAIsDead, BooleanState {<br>
+<br>
+  AAIsDeadFunction(Function &F, InformationCache &InfoCache)<br>
+      : AAIsDead(F, InfoCache) {}<br>
+<br>
+  /// See AbstractAttribute::getState()<br>
+  /// {<br>
+  AbstractState &getState() override { return *this; }<br>
+  const AbstractState &getState() const override { return *this; }<br>
+  /// }<br>
+<br>
+  /// See AbstractAttribute::getManifestPosition().<br>
+  ManifestPosition getManifestPosition() const override { return MP_FUNCTION; }<br>
+<br>
+  void initialize(Attributor &A) override {<br>
+    Function &F = getAnchorScope();<br>
+<br>
+    ToBeExploredPaths.insert(&(F.getEntryBlock().front()));<br>
+    AssumedLiveBlocks.insert(&(F.getEntryBlock()));<br>
+    for (size_t i = 0; i < ToBeExploredPaths.size(); ++i)<br>
+      explorePath(A, ToBeExploredPaths[i]);<br>
+  }<br>
+<br>
+  /// Explores new instructions starting from \p I. If instruction is dead, stop<br>
+  /// and return true if it discovered a new instruction.<br>
+  bool explorePath(Attributor &A, Instruction *I);<br>
+<br>
+  const std::string getAsStr() const override {<br>
+    return "LiveBBs(" + std::to_string(AssumedLiveBlocks.size()) + "/" +<br>
+           std::to_string(getAnchorScope().size()) + ")";<br>
+  }<br>
+<br>
+  /// See AbstractAttribute::manifest(...).<br>
+  ChangeStatus manifest(Attributor &A) override {<br>
+    assert(getState().isValidState() &&<br>
+           "Attempted to manifest an invalid state!");<br>
+<br>
+    ChangeStatus HasChanged = ChangeStatus::UNCHANGED;<br>
+<br>
+    for (Instruction *I : NoReturnCalls) {<br>
+      BasicBlock *BB = I->getParent();<br>
+<br>
+      /// Invoke is replaced with a call and unreachable is placed after it.<br>
+      if (auto *II = dyn_cast<InvokeInst>(I)) {<br>
+        changeToCall(II);<br>
+        changeToUnreachable(BB->getTerminator(), /* UseLLVMTrap */ false);<br>
+        LLVM_DEBUG(dbgs() << "[AAIsDead] Replaced invoke with call inst\n");<br>
+        continue;<br>
+      }<br>
+<br>
+      SplitBlock(BB, I->getNextNode());<br>
+      changeToUnreachable(BB->getTerminator(), /* UseLLVMTrap */ false);<br>
+      HasChanged = ChangeStatus::CHANGED;<br>
+    }<br>
+<br>
+    return HasChanged;<br>
+  }<br>
+<br>
+  /// See AbstractAttribute::updateImpl(...).<br>
+  ChangeStatus updateImpl(Attributor &A) override;<br>
+<br>
+  /// See AAIsDead::isAssumedDead().<br>
+  bool isAssumedDead(BasicBlock *BB) const override {<br>
+    if (!getAssumed())<br>
+      return false;<br>
+    return !AssumedLiveBlocks.count(BB);<br>
+  }<br>
+<br>
+  /// See AAIsDead::isKnownDead().<br>
+  bool isKnownDead(BasicBlock *BB) const override {<br>
+    if (!getKnown())<br>
+      return false;<br>
+    return !AssumedLiveBlocks.count(BB);<br>
+  }<br>
+<br>
+  /// Collection of to be explored paths.<br>
+  SmallSetVector<Instruction *, 8> ToBeExploredPaths;<br>
+<br>
+  /// Collection of all assumed live BasicBlocks.<br>
+  DenseSet<BasicBlock *> AssumedLiveBlocks;<br>
+<br>
+  /// Collection of calls with noreturn attribute, assumed or knwon.<br>
+  SmallSetVector<Instruction *, 4> NoReturnCalls;<br>
+};<br>
+<br>
+bool AAIsDeadFunction::explorePath(Attributor &A, Instruction *I) {<br>
+  BasicBlock *BB = I->getParent();<br>
+<br>
+  while (I) {<br>
+    ImmutableCallSite ICS(I);<br>
+<br>
+    if (ICS) {<br>
+      auto *NoReturnAA = A.getAAFor<AANoReturn>(*this, *I);<br>
+<br>
+      if (NoReturnAA && NoReturnAA->isAssumedNoReturn()) {<br>
+        if (!NoReturnCalls.insert(I))<br>
+          // If I is already in the NoReturnCalls set, then it stayed noreturn<br>
+          // and we didn't discover any new instructions.<br>
+          return false;<br>
+<br>
+        // Discovered new noreturn call, return true to indicate that I is not<br>
+        // noreturn anymore and should be deleted from NoReturnCalls.<br>
+        return true;<br>
+      }<br>
+<br>
+      if (ICS.hasFnAttr(Attribute::NoReturn)) {<br>
+        if(!NoReturnCalls.insert(I))<br>
+          return false;<br>
+<br>
+        return true;<br>
+      }<br>
+    }<br>
+<br>
+    I = I->getNextNode();<br>
+  }<br>
+<br>
+  // get new paths (reachable blocks).<br>
+  for (BasicBlock *SuccBB : successors(BB)) {<br>
+    Instruction *Inst = &(SuccBB->front());<br>
+    AssumedLiveBlocks.insert(SuccBB);<br>
+    ToBeExploredPaths.insert(Inst);<br>
+  }<br>
+<br>
+  return true;<br>
+}<br>
+<br>
+ChangeStatus AAIsDeadFunction::updateImpl(Attributor &A) {<br>
+  Function &F = getAnchorScope();<br></blockquote><div><br></div><div>This causes an unused variable warning in non-debug builds since F is only used at the end of the function for a debug message.  r366774 fixes this by inlining the getAnchorScope() call into the debug section. </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+<br>
+  // Temporary collection to iterate over existing noreturn instructions. This<br>
+  // will alow easier modification of NoReturnCalls collection<br>
+  SmallVector<Instruction *, 8> NoReturnChanged;<br>
+  ChangeStatus Status = ChangeStatus::UNCHANGED;<br>
+<br>
+  for (Instruction *I : NoReturnCalls)<br>
+    NoReturnChanged.push_back(I);<br>
+<br>
+  for (Instruction *I : NoReturnChanged) {<br>
+    size_t Size = ToBeExploredPaths.size();<br>
+<br>
+    // Still noreturn.<br>
+    if (!explorePath(A, I))<br>
+      continue;<br>
+<br>
+    NoReturnCalls.remove(I);<br>
+<br>
+    // No new paths.<br>
+    if (Size == ToBeExploredPaths.size())<br>
+      continue;<br>
+<br>
+    // At least one new path.<br>
+    Status = ChangeStatus::CHANGED;<br>
+<br>
+    // explore new paths.<br>
+    while (Size != ToBeExploredPaths.size())<br>
+      explorePath(A, ToBeExploredPaths[Size++]);<br>
+  }<br>
+<br>
+  LLVM_DEBUG(dbgs() << "[AAIsDead] AssumedLiveBlocks: "<br>
+                    << AssumedLiveBlocks.size()<br>
+                    << "Total number of blocks: " << F.size() << "\n");<br>
+<br>
+  return Status;<br>
+}<br>
+<br>
 /// ----------------------------------------------------------------------------<br>
 ///                               Attributor<br>
 /// ----------------------------------------------------------------------------<br>
@@ -1627,6 +1798,9 @@ void Attributor::identifyDefaultAbstract<br>
   // Every function might be "will-return".<br>
   registerAA(*new AAWillReturnFunction(F, InfoCache));<br>
<br>
+  // Check for dead BasicBlocks in every function.<br>
+  registerAA(*new AAIsDeadFunction(F, InfoCache));<br>
+<br>
   // Walk all instructions to find more attribute opportunities and also<br>
   // interesting instructions that might be queried by abstract attributes<br>
   // during their initialization or update.<br>
<br>
Modified: llvm/trunk/lib/Transforms/Utils/Local.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Utils/Local.cpp?rev=366769&r1=366768&r2=366769&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Utils/Local.cpp?rev=366769&r1=366768&r2=366769&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/Transforms/Utils/Local.cpp (original)<br>
+++ llvm/trunk/lib/Transforms/Utils/Local.cpp Mon Jul 22 16:58:23 2019<br>
@@ -1964,7 +1964,7 @@ unsigned llvm::changeToUnreachable(Instr<br>
 }<br>
<br>
 /// changeToCall - Convert the specified invoke into a normal call.<br>
-static void changeToCall(InvokeInst *II, DomTreeUpdater *DTU = nullptr) {<br>
+void llvm::changeToCall(InvokeInst *II, DomTreeUpdater *DTU) {<br>
   SmallVector<Value*, 8> Args(II->arg_begin(), II->arg_end());<br>
   SmallVector<OperandBundleDef, 1> OpBundles;<br>
   II->getOperandBundlesAsDefs(OpBundles);<br>
<br>
Added: llvm/trunk/test/Transforms/FunctionAttrs/liveness.ll<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/FunctionAttrs/liveness.ll?rev=366769&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/FunctionAttrs/liveness.ll?rev=366769&view=auto</a><br>
==============================================================================<br>
--- llvm/trunk/test/Transforms/FunctionAttrs/liveness.ll (added)<br>
+++ llvm/trunk/test/Transforms/FunctionAttrs/liveness.ll Mon Jul 22 16:58:23 2019<br>
@@ -0,0 +1,250 @@<br>
+; RUN: opt -attributor --attributor-disable=false -S < %s | FileCheck %s<br>
+<br>
+declare void @no_return_call() noreturn<br>
+<br>
+declare void @normal_call()<br>
+<br>
+declare i32 @foo()<br>
+<br>
+declare i32 @foo_noreturn() noreturn<br>
+<br>
+declare i32 @bar()<br>
+<br>
+; TEST 1: cond.true is dead, but cond.end is not, since cond.false is live<br>
+<br>
+; This is just an example. For example we can put a sync call in a<br>
+; dead block and check if it is deduced.<br>
+<br>
+define i32 @dead_block_present(i32 %a) #0 {<br>
+entry:<br>
+  %cmp = icmp eq i32 %a, 0<br>
+  br i1 %cmp, label %cond.true, label %cond.false<br>
+<br>
+cond.true:                                        ; preds = %entry<br>
+  call void @no_return_call()<br>
+  ; CHECK: call void @no_return_call()<br>
+  ; CHECK-NEXT: unreachable<br>
+  %call = call i32 @foo()<br>
+  br label %cond.end<br>
+<br>
+cond.false:                                       ; preds = %entry<br>
+  call void @normal_call()<br>
+  %call1 = call i32 @bar()<br>
+  br label %cond.end<br>
+<br>
+cond.end:                                         ; preds = %cond.false, %cond.true<br>
+  %cond = phi i32 [ %call, %cond.true ], [ %call1, %cond.false ]<br>
+  ret i32 %cond<br>
+}<br>
+<br>
+; TEST 2: both cond.true and cond.false are dead, therfore cond.end is dead as well.<br>
+<br>
+define i32 @all_dead(i32 %a) #0 {<br>
+entry:<br>
+  %cmp = icmp eq i32 %a, 0<br>
+  br i1 %cmp, label %cond.true, label %cond.false<br>
+<br>
+cond.true:                                        ; preds = %entry<br>
+  call void @no_return_call()<br>
+  ; CHECK: call void @no_return_call()<br>
+  ; CHECK-NEXT: unreachable<br>
+  %call = call i32 @foo()<br>
+  br label %cond.end<br>
+<br>
+cond.false:                                       ; preds = %entry<br>
+  call void @no_return_call()<br>
+  ; CHECK: call void @no_return_call()<br>
+  ; CHECK-NEXT: unreachable<br>
+  %call1 = call i32 @bar()<br>
+  br label %cond.end<br>
+<br>
+cond.end:                                         ; preds = %cond.false, %cond.true<br>
+  %cond = phi i32 [ %call, %cond.true ], [ %call1, %cond.false ]<br>
+  ret i32 %cond<br>
+}<br>
+<br>
+declare i32 @__gxx_personality_v0(...)<br>
+<br>
+; TEST 3: All blocks are live.<br>
+<br>
+; CHECK: define i32 @all_live(i32 %a)<br>
+define i32 @all_live(i32 %a) #0 {<br>
+entry:<br>
+  %cmp = icmp eq i32 %a, 0<br>
+  br i1 %cmp, label %cond.true, label %cond.false<br>
+<br>
+cond.true:                                        ; preds = %entry<br>
+  call void @normal_call()<br>
+  %call = call i32 @foo_noreturn()<br>
+  br label %cond.end<br>
+<br>
+cond.false:                                       ; preds = %entry<br>
+  call void @normal_call()<br>
+  %call1 = call i32 @bar()<br>
+  br label %cond.end<br>
+<br>
+cond.end:                                         ; preds = %cond.false, %cond.true<br>
+  %cond = phi i32 [ %call, %cond.true ], [ %call1, %cond.false ]<br>
+  ret i32 %cond<br>
+}<br>
+<br>
+; TEST 4 noreturn invoke instruction replaced by a call and an unreachable instruction<br>
+; put after it.<br>
+<br>
+; CHECK: define i32 @invoke_noreturn(i32 %a)<br>
+define i32 @invoke_noreturn(i32 %a) personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {<br>
+entry:<br>
+  %cmp = icmp eq i32 %a, 0<br>
+  br i1 %cmp, label %cond.true, label %cond.false<br>
+<br>
+cond.true:                                        ; preds = %entry<br>
+  call void @normal_call()<br>
+  %call = invoke i32 @foo_noreturn() to label %continue<br>
+            unwind label %cleanup<br>
+  ; CHECK: call i32 @foo_noreturn()<br>
+  ; CHECK-NEXT unreachable<br>
+<br>
+cond.false:                                       ; preds = %entry<br>
+  call void @normal_call()<br>
+  %call1 = call i32 @bar()<br>
+  br label %cond.end<br>
+<br>
+cond.end:                                         ; preds = %cond.false, %continue<br>
+  %cond = phi i32 [ %call, %continue ], [ %call1, %cond.false ]<br>
+  ret i32 %cond<br>
+<br>
+continue:<br>
+  br label %cond.end<br>
+<br>
+cleanup:<br>
+  %res = landingpad { i8*, i32 }<br>
+  catch i8* null<br>
+  ret i32 0<br>
+}<br>
+<br>
+; TEST 5: Undefined behvior, taken from LangRef.<br>
+; FIXME: Should be able to detect undefined behavior.<br>
+<br>
+; CHECK define @ub(i32)<br>
+define void @ub(i32* ) {<br>
+  %poison = sub nuw i32 0, 1           ; Results in a poison value.<br>
+  %still_poison = and i32 %poison, 0   ; 0, but also poison.<br>
+  %poison_yet_again = getelementptr i32, i32* %0, i32 %still_poison<br>
+  store i32 0, i32* %poison_yet_again  ; Undefined behavior due to store to poison.<br>
+  ret void<br>
+}<br>
+<br>
+define void @inf_loop() #0 {<br>
+entry:<br>
+  br label %while.body<br>
+<br>
+while.body:                                       ; preds = %entry, %while.body<br>
+  br label %while.body<br>
+}<br>
+<br>
+; TEST 6: Infinite loop.<br>
+; FIXME: Detect infloops, and mark affected blocks dead.<br>
+<br>
+define i32 @test5(i32, i32) #0 {<br>
+  %3 = icmp sgt i32 %0, %1<br>
+  br i1 %3, label %cond.if, label %cond.elseif<br>
+<br>
+cond.if:                                                ; preds = %2<br>
+  %4 = tail call i32 @bar()<br>
+  br label %cond.end<br>
+<br>
+cond.elseif:                                                ; preds = %2<br>
+  call void @inf_loop()<br>
+  %5 = icmp slt i32 %0, %1<br>
+  br i1 %5, label %cond.end, label %cond.else<br>
+<br>
+cond.else:                                                ; preds = %cond.elseif<br>
+  %6 = tail call i32 @foo()<br>
+  br label %cond.end<br>
+<br>
+cond.end:                                               ; preds = %cond.if, %cond.else, %cond.elseif<br>
+  %7 = phi i32 [ %1, %cond.elseif ], [ 0, %cond.else ], [ 0, %cond.if ]<br>
+  ret i32 %7<br>
+}<br>
+<br>
+define void @rec() #0 {<br>
+entry:<br>
+  call void @rec()<br>
+  ret void<br>
+}<br>
+<br>
+; TEST 7: Recursion<br>
+; FIXME: everything after first block should be marked dead<br>
+; and unreachable should be put after call to @rec().<br>
+<br>
+define i32 @test6(i32, i32) #0 {<br>
+  call void @rec()<br>
+  %3 = icmp sgt i32 %0, %1<br>
+  br i1 %3, label %cond.if, label %cond.elseif<br>
+<br>
+cond.if:                                                ; preds = %2<br>
+  %4 = tail call i32 @bar()<br>
+  br label %cond.end<br>
+<br>
+cond.elseif:                                                ; preds = %2<br>
+  call void @rec()<br>
+  %5 = icmp slt i32 %0, %1<br>
+  br i1 %5, label %cond.end, label %cond.else<br>
+<br>
+cond.else:                                                ; preds = %cond.elseif<br>
+  %6 = tail call i32 @foo()<br>
+  br label %cond.end<br>
+<br>
+cond.end:                                               ; preds = %cond.if, %cond.else, %cond.elseif<br>
+  %7 = phi i32 [ %1, %cond.elseif ], [ 0, %cond.else ], [ 0, %cond.if ]<br>
+  ret i32 %7<br>
+}<br>
+; TEST 8: Recursion<br>
+; FIXME: contains recursive call to itself in cond.elseif block<br>
+<br>
+define i32 @test7(i32, i32) #0 {<br>
+  %3 = icmp sgt i32 %0, %1<br>
+  br i1 %3, label %cond.if, label %cond.elseif<br>
+<br>
+cond.if:                                                ; preds = %2<br>
+  %4 = tail call i32 @bar()<br>
+  br label %cond.end<br>
+<br>
+cond.elseif:                                                ; preds = %2<br>
+  %5 = tail call i32 @test7(i32 %0, i32 %1)<br>
+  %6 = icmp slt i32 %0, %1<br>
+  br i1 %6, label %cond.end, label %cond.else<br>
+<br>
+cond.else:                                                ; preds = %cond.elseif<br>
+  %7 = tail call i32 @foo()<br>
+  br label %cond.end<br>
+<br>
+cond.end:                                               ; preds = %cond.if, %cond.else, %cond.elseif<br>
+  %8 = phi i32 [ %1, %cond.elseif ], [ 0, %cond.else ], [ 0, %cond.if ]<br>
+  ret i32 %8<br>
+}<br>
+<br>
+; TEST 9: Only first block is live.<br>
+<br>
+define i32 @first_block_no_return(i32 %a) #0 {<br>
+entry:<br>
+  call void @no_return_call()<br>
+  ; CHECK: call void @no_return_call()<br>
+  ; CHECK-NEXT: unreachable<br>
+  %cmp = icmp eq i32 %a, 0<br>
+  br i1 %cmp, label %cond.true, label %cond.false<br>
+<br>
+cond.true:                                        ; preds = %entry<br>
+  call void @normal_call()<br>
+  %call = call i32 @foo()<br>
+  br label %cond.end<br>
+<br>
+cond.false:                                       ; preds = %entry<br>
+  call void @normal_call()<br>
+  %call1 = call i32 @bar()<br>
+  br label %cond.end<br>
+<br>
+cond.end:                                         ; preds = %cond.false, %cond.true<br>
+  %cond = phi i32 [ %call, %cond.true ], [ %call1, %cond.false ]<br>
+  ret i32 %cond<br>
+}<br>
<br>
<br>
_______________________________________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits</a><br>
</blockquote></div></div>