[llvm] r366769 - Fixing build error from commit 95cbc3d

Stefan Stipanovic via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 22 16:58:23 PDT 2019


Author: sstefan
Date: Mon Jul 22 16:58:23 2019
New Revision: 366769

URL: http://llvm.org/viewvc/llvm-project?rev=366769&view=rev
Log:
Fixing build error from commit 95cbc3d

[Attributor] Liveness analysis.

Liveness analysis abstract attribute used to indicate which BasicBlocks are dead and can therefore be ignored.
Right now we are only looking at noreturn calls.

Reviewers: jdoerfert, uenoku

Subscribers: hiraditya, llvm-commits

Differential Revision: https://reviews.llvm.org/D64162

Added:
    llvm/trunk/test/Transforms/FunctionAttrs/liveness.ll
Modified:
    llvm/trunk/include/llvm/Transforms/IPO/Attributor.h
    llvm/trunk/include/llvm/Transforms/Utils/Local.h
    llvm/trunk/lib/Transforms/IPO/Attributor.cpp
    llvm/trunk/lib/Transforms/Utils/Local.cpp

Modified: llvm/trunk/include/llvm/Transforms/IPO/Attributor.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Transforms/IPO/Attributor.h?rev=366769&r1=366768&r2=366769&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Transforms/IPO/Attributor.h (original)
+++ llvm/trunk/include/llvm/Transforms/IPO/Attributor.h Mon Jul 22 16:58:23 2019
@@ -805,6 +805,46 @@ struct AANoAlias : public AbstractAttrib
   static constexpr Attribute::AttrKind ID = Attribute::NoAlias;
 };
 
+/// An AbstractAttribute for noreturn.
+struct AANoReturn : public AbstractAttribute {
+
+  /// See AbstractAttribute::AbstractAttribute(...).
+  AANoReturn(Value &V, InformationCache &InfoCache)
+      : AbstractAttribute(V, InfoCache) {}
+
+  /// Return true if the underlying object is known to never return.
+  virtual bool isKnownNoReturn() const = 0;
+
+  /// Return true if the underlying object is assumed to never return.
+  virtual bool isAssumedNoReturn() const = 0;
+
+  /// See AbstractAttribute::getAttrKind()
+  Attribute::AttrKind getAttrKind() const override { return ID; }
+
+  /// The identifier used by the Attributor for this class of attributes.
+  static constexpr Attribute::AttrKind ID = Attribute::NoReturn;
+};
+
+/// An abstract interface for liveness abstract attribute.
+struct AAIsDead : public AbstractAttribute {
+
+  /// See AbstractAttribute::AbstractAttribute(...).
+  AAIsDead(Value &V, InformationCache &InfoCache)
+      : AbstractAttribute(V, InfoCache) {}
+
+  /// See AbstractAttribute::getAttrKind()
+  Attribute::AttrKind getAttrKind() const override { return ID; }
+
+  static constexpr Attribute::AttrKind ID =
+      Attribute::AttrKind(Attribute::EndAttrKinds + 1);
+
+  /// Returns true if \p BB is assumed dead.
+  virtual bool isAssumedDead(BasicBlock *BB) const = 0;
+
+  /// Returns true if \p BB is known dead.
+  virtual bool isKnownDead(BasicBlock *BB) const = 0;
+};
+
 } // end namespace llvm
 
 #endif // LLVM_TRANSFORMS_IPO_FUNCTIONATTRS_H

Modified: llvm/trunk/include/llvm/Transforms/Utils/Local.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Transforms/Utils/Local.h?rev=366769&r1=366768&r2=366769&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Transforms/Utils/Local.h (original)
+++ llvm/trunk/include/llvm/Transforms/Utils/Local.h Mon Jul 22 16:58:23 2019
@@ -271,6 +271,9 @@ inline unsigned getKnownAlignment(Value
   return getOrEnforceKnownAlignment(V, 0, DL, CxtI, AC, DT);
 }
 
+/// This function converts the specified invoek into a normall call.
+void changeToCall(InvokeInst *II, DomTreeUpdater *DTU = nullptr);
+
 ///===---------------------------------------------------------------------===//
 ///  Dbg Intrinsic utilities
 ///

Modified: llvm/trunk/lib/Transforms/IPO/Attributor.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/Attributor.cpp?rev=366769&r1=366768&r2=366769&view=diff
==============================================================================
--- llvm/trunk/lib/Transforms/IPO/Attributor.cpp (original)
+++ llvm/trunk/lib/Transforms/IPO/Attributor.cpp Mon Jul 22 16:58:23 2019
@@ -16,6 +16,7 @@
 #include "llvm/Transforms/IPO/Attributor.h"
 
 #include "llvm/ADT/DepthFirstIterator.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallVector.h"
@@ -31,6 +32,9 @@
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/raw_ostream.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+#include "llvm/Transforms/Utils/Local.h"
+
 #include <cassert>
 
 using namespace llvm;
@@ -1411,6 +1415,173 @@ ChangeStatus AANoAliasReturned::updateIm
   return ChangeStatus::UNCHANGED;
 }
 
+/// -------------------AAIsDead Function Attribute-----------------------
+
+struct AAIsDeadFunction : AAIsDead, BooleanState {
+
+  AAIsDeadFunction(Function &F, InformationCache &InfoCache)
+      : AAIsDead(F, InfoCache) {}
+
+  /// See AbstractAttribute::getState()
+  /// {
+  AbstractState &getState() override { return *this; }
+  const AbstractState &getState() const override { return *this; }
+  /// }
+
+  /// See AbstractAttribute::getManifestPosition().
+  ManifestPosition getManifestPosition() const override { return MP_FUNCTION; }
+
+  void initialize(Attributor &A) override {
+    Function &F = getAnchorScope();
+
+    ToBeExploredPaths.insert(&(F.getEntryBlock().front()));
+    AssumedLiveBlocks.insert(&(F.getEntryBlock()));
+    for (size_t i = 0; i < ToBeExploredPaths.size(); ++i)
+      explorePath(A, ToBeExploredPaths[i]);
+  }
+
+  /// Explores new instructions starting from \p I. If instruction is dead, stop
+  /// and return true if it discovered a new instruction.
+  bool explorePath(Attributor &A, Instruction *I);
+
+  const std::string getAsStr() const override {
+    return "LiveBBs(" + std::to_string(AssumedLiveBlocks.size()) + "/" +
+           std::to_string(getAnchorScope().size()) + ")";
+  }
+
+  /// See AbstractAttribute::manifest(...).
+  ChangeStatus manifest(Attributor &A) override {
+    assert(getState().isValidState() &&
+           "Attempted to manifest an invalid state!");
+
+    ChangeStatus HasChanged = ChangeStatus::UNCHANGED;
+
+    for (Instruction *I : NoReturnCalls) {
+      BasicBlock *BB = I->getParent();
+
+      /// Invoke is replaced with a call and unreachable is placed after it.
+      if (auto *II = dyn_cast<InvokeInst>(I)) {
+        changeToCall(II);
+        changeToUnreachable(BB->getTerminator(), /* UseLLVMTrap */ false);
+        LLVM_DEBUG(dbgs() << "[AAIsDead] Replaced invoke with call inst\n");
+        continue;
+      }
+
+      SplitBlock(BB, I->getNextNode());
+      changeToUnreachable(BB->getTerminator(), /* UseLLVMTrap */ false);
+      HasChanged = ChangeStatus::CHANGED;
+    }
+
+    return HasChanged;
+  }
+
+  /// See AbstractAttribute::updateImpl(...).
+  ChangeStatus updateImpl(Attributor &A) override;
+
+  /// See AAIsDead::isAssumedDead().
+  bool isAssumedDead(BasicBlock *BB) const override {
+    if (!getAssumed())
+      return false;
+    return !AssumedLiveBlocks.count(BB);
+  }
+
+  /// See AAIsDead::isKnownDead().
+  bool isKnownDead(BasicBlock *BB) const override {
+    if (!getKnown())
+      return false;
+    return !AssumedLiveBlocks.count(BB);
+  }
+
+  /// Collection of to be explored paths.
+  SmallSetVector<Instruction *, 8> ToBeExploredPaths;
+
+  /// Collection of all assumed live BasicBlocks.
+  DenseSet<BasicBlock *> AssumedLiveBlocks;
+
+  /// Collection of calls with noreturn attribute, assumed or knwon.
+  SmallSetVector<Instruction *, 4> NoReturnCalls;
+};
+
+bool AAIsDeadFunction::explorePath(Attributor &A, Instruction *I) {
+  BasicBlock *BB = I->getParent();
+
+  while (I) {
+    ImmutableCallSite ICS(I);
+
+    if (ICS) {
+      auto *NoReturnAA = A.getAAFor<AANoReturn>(*this, *I);
+
+      if (NoReturnAA && NoReturnAA->isAssumedNoReturn()) {
+        if (!NoReturnCalls.insert(I))
+          // If I is already in the NoReturnCalls set, then it stayed noreturn
+          // and we didn't discover any new instructions.
+          return false;
+
+        // Discovered new noreturn call, return true to indicate that I is not
+        // noreturn anymore and should be deleted from NoReturnCalls.
+        return true;
+      }
+
+      if (ICS.hasFnAttr(Attribute::NoReturn)) {
+        if(!NoReturnCalls.insert(I))
+          return false;
+
+        return true;
+      }
+    }
+
+    I = I->getNextNode();
+  }
+
+  // get new paths (reachable blocks).
+  for (BasicBlock *SuccBB : successors(BB)) {
+    Instruction *Inst = &(SuccBB->front());
+    AssumedLiveBlocks.insert(SuccBB);
+    ToBeExploredPaths.insert(Inst);
+  }
+
+  return true;
+}
+
+ChangeStatus AAIsDeadFunction::updateImpl(Attributor &A) {
+  Function &F = getAnchorScope();
+
+  // Temporary collection to iterate over existing noreturn instructions. This
+  // will alow easier modification of NoReturnCalls collection
+  SmallVector<Instruction *, 8> NoReturnChanged;
+  ChangeStatus Status = ChangeStatus::UNCHANGED;
+
+  for (Instruction *I : NoReturnCalls)
+    NoReturnChanged.push_back(I);
+
+  for (Instruction *I : NoReturnChanged) {
+    size_t Size = ToBeExploredPaths.size();
+
+    // Still noreturn.
+    if (!explorePath(A, I))
+      continue;
+
+    NoReturnCalls.remove(I);
+
+    // No new paths.
+    if (Size == ToBeExploredPaths.size())
+      continue;
+
+    // At least one new path.
+    Status = ChangeStatus::CHANGED;
+
+    // explore new paths.
+    while (Size != ToBeExploredPaths.size())
+      explorePath(A, ToBeExploredPaths[Size++]);
+  }
+
+  LLVM_DEBUG(dbgs() << "[AAIsDead] AssumedLiveBlocks: "
+                    << AssumedLiveBlocks.size()
+                    << "Total number of blocks: " << F.size() << "\n");
+
+  return Status;
+}
+
 /// ----------------------------------------------------------------------------
 ///                               Attributor
 /// ----------------------------------------------------------------------------
@@ -1627,6 +1798,9 @@ void Attributor::identifyDefaultAbstract
   // Every function might be "will-return".
   registerAA(*new AAWillReturnFunction(F, InfoCache));
 
+  // Check for dead BasicBlocks in every function.
+  registerAA(*new AAIsDeadFunction(F, InfoCache));
+
   // Walk all instructions to find more attribute opportunities and also
   // interesting instructions that might be queried by abstract attributes
   // during their initialization or update.

Modified: llvm/trunk/lib/Transforms/Utils/Local.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Utils/Local.cpp?rev=366769&r1=366768&r2=366769&view=diff
==============================================================================
--- llvm/trunk/lib/Transforms/Utils/Local.cpp (original)
+++ llvm/trunk/lib/Transforms/Utils/Local.cpp Mon Jul 22 16:58:23 2019
@@ -1964,7 +1964,7 @@ unsigned llvm::changeToUnreachable(Instr
 }
 
 /// changeToCall - Convert the specified invoke into a normal call.
-static void changeToCall(InvokeInst *II, DomTreeUpdater *DTU = nullptr) {
+void llvm::changeToCall(InvokeInst *II, DomTreeUpdater *DTU) {
   SmallVector<Value*, 8> Args(II->arg_begin(), II->arg_end());
   SmallVector<OperandBundleDef, 1> OpBundles;
   II->getOperandBundlesAsDefs(OpBundles);

Added: llvm/trunk/test/Transforms/FunctionAttrs/liveness.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/FunctionAttrs/liveness.ll?rev=366769&view=auto
==============================================================================
--- llvm/trunk/test/Transforms/FunctionAttrs/liveness.ll (added)
+++ llvm/trunk/test/Transforms/FunctionAttrs/liveness.ll Mon Jul 22 16:58:23 2019
@@ -0,0 +1,250 @@
+; RUN: opt -attributor --attributor-disable=false -S < %s | FileCheck %s
+
+declare void @no_return_call() noreturn
+
+declare void @normal_call()
+
+declare i32 @foo()
+
+declare i32 @foo_noreturn() noreturn
+
+declare i32 @bar()
+
+; TEST 1: cond.true is dead, but cond.end is not, since cond.false is live
+
+; This is just an example. For example we can put a sync call in a
+; dead block and check if it is deduced.
+
+define i32 @dead_block_present(i32 %a) #0 {
+entry:
+  %cmp = icmp eq i32 %a, 0
+  br i1 %cmp, label %cond.true, label %cond.false
+
+cond.true:                                        ; preds = %entry
+  call void @no_return_call()
+  ; CHECK: call void @no_return_call()
+  ; CHECK-NEXT: unreachable
+  %call = call i32 @foo()
+  br label %cond.end
+
+cond.false:                                       ; preds = %entry
+  call void @normal_call()
+  %call1 = call i32 @bar()
+  br label %cond.end
+
+cond.end:                                         ; preds = %cond.false, %cond.true
+  %cond = phi i32 [ %call, %cond.true ], [ %call1, %cond.false ]
+  ret i32 %cond
+}
+
+; TEST 2: both cond.true and cond.false are dead, therfore cond.end is dead as well.
+
+define i32 @all_dead(i32 %a) #0 {
+entry:
+  %cmp = icmp eq i32 %a, 0
+  br i1 %cmp, label %cond.true, label %cond.false
+
+cond.true:                                        ; preds = %entry
+  call void @no_return_call()
+  ; CHECK: call void @no_return_call()
+  ; CHECK-NEXT: unreachable
+  %call = call i32 @foo()
+  br label %cond.end
+
+cond.false:                                       ; preds = %entry
+  call void @no_return_call()
+  ; CHECK: call void @no_return_call()
+  ; CHECK-NEXT: unreachable
+  %call1 = call i32 @bar()
+  br label %cond.end
+
+cond.end:                                         ; preds = %cond.false, %cond.true
+  %cond = phi i32 [ %call, %cond.true ], [ %call1, %cond.false ]
+  ret i32 %cond
+}
+
+declare i32 @__gxx_personality_v0(...)
+
+; TEST 3: All blocks are live.
+
+; CHECK: define i32 @all_live(i32 %a)
+define i32 @all_live(i32 %a) #0 {
+entry:
+  %cmp = icmp eq i32 %a, 0
+  br i1 %cmp, label %cond.true, label %cond.false
+
+cond.true:                                        ; preds = %entry
+  call void @normal_call()
+  %call = call i32 @foo_noreturn()
+  br label %cond.end
+
+cond.false:                                       ; preds = %entry
+  call void @normal_call()
+  %call1 = call i32 @bar()
+  br label %cond.end
+
+cond.end:                                         ; preds = %cond.false, %cond.true
+  %cond = phi i32 [ %call, %cond.true ], [ %call1, %cond.false ]
+  ret i32 %cond
+}
+
+; TEST 4 noreturn invoke instruction replaced by a call and an unreachable instruction
+; put after it.
+
+; CHECK: define i32 @invoke_noreturn(i32 %a)
+define i32 @invoke_noreturn(i32 %a) personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
+entry:
+  %cmp = icmp eq i32 %a, 0
+  br i1 %cmp, label %cond.true, label %cond.false
+
+cond.true:                                        ; preds = %entry
+  call void @normal_call()
+  %call = invoke i32 @foo_noreturn() to label %continue
+            unwind label %cleanup
+  ; CHECK: call i32 @foo_noreturn()
+  ; CHECK-NEXT unreachable
+
+cond.false:                                       ; preds = %entry
+  call void @normal_call()
+  %call1 = call i32 @bar()
+  br label %cond.end
+
+cond.end:                                         ; preds = %cond.false, %continue
+  %cond = phi i32 [ %call, %continue ], [ %call1, %cond.false ]
+  ret i32 %cond
+
+continue:
+  br label %cond.end
+
+cleanup:
+  %res = landingpad { i8*, i32 }
+  catch i8* null
+  ret i32 0
+}
+
+; TEST 5: Undefined behvior, taken from LangRef.
+; FIXME: Should be able to detect undefined behavior.
+
+; CHECK define @ub(i32)
+define void @ub(i32* ) {
+  %poison = sub nuw i32 0, 1           ; Results in a poison value.
+  %still_poison = and i32 %poison, 0   ; 0, but also poison.
+  %poison_yet_again = getelementptr i32, i32* %0, i32 %still_poison
+  store i32 0, i32* %poison_yet_again  ; Undefined behavior due to store to poison.
+  ret void
+}
+
+define void @inf_loop() #0 {
+entry:
+  br label %while.body
+
+while.body:                                       ; preds = %entry, %while.body
+  br label %while.body
+}
+
+; TEST 6: Infinite loop.
+; FIXME: Detect infloops, and mark affected blocks dead.
+
+define i32 @test5(i32, i32) #0 {
+  %3 = icmp sgt i32 %0, %1
+  br i1 %3, label %cond.if, label %cond.elseif
+
+cond.if:                                                ; preds = %2
+  %4 = tail call i32 @bar()
+  br label %cond.end
+
+cond.elseif:                                                ; preds = %2
+  call void @inf_loop()
+  %5 = icmp slt i32 %0, %1
+  br i1 %5, label %cond.end, label %cond.else
+
+cond.else:                                                ; preds = %cond.elseif
+  %6 = tail call i32 @foo()
+  br label %cond.end
+
+cond.end:                                               ; preds = %cond.if, %cond.else, %cond.elseif
+  %7 = phi i32 [ %1, %cond.elseif ], [ 0, %cond.else ], [ 0, %cond.if ]
+  ret i32 %7
+}
+
+define void @rec() #0 {
+entry:
+  call void @rec()
+  ret void
+}
+
+; TEST 7: Recursion
+; FIXME: everything after first block should be marked dead
+; and unreachable should be put after call to @rec().
+
+define i32 @test6(i32, i32) #0 {
+  call void @rec()
+  %3 = icmp sgt i32 %0, %1
+  br i1 %3, label %cond.if, label %cond.elseif
+
+cond.if:                                                ; preds = %2
+  %4 = tail call i32 @bar()
+  br label %cond.end
+
+cond.elseif:                                                ; preds = %2
+  call void @rec()
+  %5 = icmp slt i32 %0, %1
+  br i1 %5, label %cond.end, label %cond.else
+
+cond.else:                                                ; preds = %cond.elseif
+  %6 = tail call i32 @foo()
+  br label %cond.end
+
+cond.end:                                               ; preds = %cond.if, %cond.else, %cond.elseif
+  %7 = phi i32 [ %1, %cond.elseif ], [ 0, %cond.else ], [ 0, %cond.if ]
+  ret i32 %7
+}
+; TEST 8: Recursion
+; FIXME: contains recursive call to itself in cond.elseif block
+
+define i32 @test7(i32, i32) #0 {
+  %3 = icmp sgt i32 %0, %1
+  br i1 %3, label %cond.if, label %cond.elseif
+
+cond.if:                                                ; preds = %2
+  %4 = tail call i32 @bar()
+  br label %cond.end
+
+cond.elseif:                                                ; preds = %2
+  %5 = tail call i32 @test7(i32 %0, i32 %1)
+  %6 = icmp slt i32 %0, %1
+  br i1 %6, label %cond.end, label %cond.else
+
+cond.else:                                                ; preds = %cond.elseif
+  %7 = tail call i32 @foo()
+  br label %cond.end
+
+cond.end:                                               ; preds = %cond.if, %cond.else, %cond.elseif
+  %8 = phi i32 [ %1, %cond.elseif ], [ 0, %cond.else ], [ 0, %cond.if ]
+  ret i32 %8
+}
+
+; TEST 9: Only first block is live.
+
+define i32 @first_block_no_return(i32 %a) #0 {
+entry:
+  call void @no_return_call()
+  ; CHECK: call void @no_return_call()
+  ; CHECK-NEXT: unreachable
+  %cmp = icmp eq i32 %a, 0
+  br i1 %cmp, label %cond.true, label %cond.false
+
+cond.true:                                        ; preds = %entry
+  call void @normal_call()
+  %call = call i32 @foo()
+  br label %cond.end
+
+cond.false:                                       ; preds = %entry
+  call void @normal_call()
+  %call1 = call i32 @bar()
+  br label %cond.end
+
+cond.end:                                         ; preds = %cond.false, %cond.true
+  %cond = phi i32 [ %call, %cond.true ], [ %call1, %cond.false ]
+  ret i32 %cond
+}




More information about the llvm-commits mailing list