<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>