[llvm] cd4aab4 - [Attributor] Liveness for values

Johannes Doerfert via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 30 22:17:01 PDT 2019


Author: Johannes Doerfert
Date: 2019-10-31T00:16:36-05:00
New Revision: cd4aab4a8ac43dd661f132fd940bc80828788fda

URL: https://github.com/llvm/llvm-project/commit/cd4aab4a8ac43dd661f132fd940bc80828788fda
DIFF: https://github.com/llvm/llvm-project/commit/cd4aab4a8ac43dd661f132fd940bc80828788fda.diff

LOG: [Attributor] Liveness for values

Summary:
This patch introduces liveness (AAIsDead) for all positions, thus for
all kinds of values. For now, we say an instruction is dead if it would
be removed assuming all users are dead. A call site return is different
as we just look at the users. If all call site returns have been
eliminated, the return values can return undef instead of their original
value, eliminating uses.

We try to recursively delete dead instructions now and we introduce a
simple check interface for use-traversal.

This is the idea tried out in D68626 but implemented in the right way.

Reviewers: uenoku, sstefan1

Subscribers: hiraditya, bollu, llvm-commits

Tags: #llvm

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

Added: 
    

Modified: 
    llvm/include/llvm/Transforms/IPO/Attributor.h
    llvm/lib/Transforms/IPO/Attributor.cpp
    llvm/test/Transforms/FunctionAttrs/align.ll
    llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
    llvm/test/Transforms/FunctionAttrs/arg_returned.ll
    llvm/test/Transforms/FunctionAttrs/liveness.ll
    llvm/test/Transforms/FunctionAttrs/misc.ll
    llvm/test/Transforms/FunctionAttrs/new_attributes.ll
    llvm/test/Transforms/FunctionAttrs/noalias_returned.ll
    llvm/test/Transforms/FunctionAttrs/nonnull.ll

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h
index 17cc11491fda..d3c16acb1a1f 100644
--- a/llvm/include/llvm/Transforms/IPO/Attributor.h
+++ b/llvm/include/llvm/Transforms/IPO/Attributor.h
@@ -789,10 +789,25 @@ struct Attributor {
     identifyDefaultAbstractAttributes(const_cast<Function &>(F));
   }
 
-  /// Record that \p I is deleted after information was manifested.
+  /// Record that \p U is to be replaces with \p NV after information was
+  /// manifested. This also triggers deletion of trivially dead istructions.
+  bool changeUseAfterManifest(Use &U, Value &NV) {
+    Value *&V = ToBeChangedUses[&U];
+    if (V && (V->stripPointerCasts() == NV.stripPointerCasts() ||
+              isa_and_nonnull<UndefValue>(V)))
+      return false;
+    assert((!V || V == &NV || isa<UndefValue>(NV)) &&
+           "Use was registered twice for replacement with 
diff erent values!");
+    V = &NV;
+    return true;
+  }
+
+  /// Record that \p I is deleted after information was manifested. This also
+  /// triggers deletion of trivially dead istructions.
   void deleteAfterManifest(Instruction &I) { ToBeDeletedInsts.insert(&I); }
 
-  /// Record that \p BB is deleted after information was manifested.
+  /// Record that \p BB is deleted after information was manifested. This also
+  /// triggers deletion of trivially dead istructions.
   void deleteAfterManifest(BasicBlock &BB) { ToBeDeletedBlocks.insert(&BB); }
 
   /// Record that \p F is deleted after information was manifested.
@@ -803,6 +818,13 @@ struct Attributor {
   /// If \p LivenessAA is not provided it is queried.
   bool isAssumedDead(const AbstractAttribute &AA, const AAIsDead *LivenessAA);
 
+  /// Check \p Pred on all (transitive) uses of \p V.
+  ///
+  /// This method will evaluate \p Pred on all (transitive) uses of the
+  /// associated value and return true if \p Pred holds every time.
+  bool checkForAllUses(const function_ref<bool(const Use &, bool &)> &Pred,
+                       const AbstractAttribute &QueryingAA, const Value &V);
+
   /// Check \p Pred on all function call sites.
   ///
   /// This method will evaluate \p Pred on call sites and return
@@ -972,6 +994,10 @@ struct Attributor {
   /// A set to remember the functions we already assume to be live and visited.
   DenseSet<const Function *> VisitedFunctions;
 
+  /// Uses we replace with a new value after manifest is done. We will remove
+  /// then trivially dead instructions as well.
+  DenseMap<Use *, Value *> ToBeChangedUses;
+
   /// Functions, blocks, and instructions we delete after manifest is done.
   ///
   ///{
@@ -1683,6 +1709,9 @@ struct AAIsDead : public StateWrapper<BooleanState, AbstractAttribute>,
                   public IRPosition {
   AAIsDead(const IRPosition &IRP) : IRPosition(IRP) {}
 
+  /// Returns true if the underlying value is assumed dead.
+  virtual bool isAssumedDead() const = 0;
+
   /// Returns true if \p BB is assumed dead.
   virtual bool isAssumedDead(const BasicBlock *BB) const = 0;
 

diff  --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp
index 129eb1976745..e18e4ccefc7c 100644
--- a/llvm/lib/Transforms/IPO/Attributor.cpp
+++ b/llvm/lib/Transforms/IPO/Attributor.cpp
@@ -2095,9 +2095,204 @@ struct AANoAliasCallSiteReturned final : AANoAliasImpl {
 
 /// -------------------AAIsDead Function Attribute-----------------------
 
-struct AAIsDeadImpl : public AAIsDead {
-  AAIsDeadImpl(const IRPosition &IRP) : AAIsDead(IRP) {}
+struct AAIsDeadValueImpl : public AAIsDead {
+  AAIsDeadValueImpl(const IRPosition &IRP) : AAIsDead(IRP) {}
 
+  /// See AAIsDead::isAssumedDead().
+  bool isAssumedDead() const override { return getAssumed(); }
+
+  /// See AAIsDead::isAssumedDead(BasicBlock *).
+  bool isAssumedDead(const BasicBlock *BB) const override { return false; }
+
+  /// See AAIsDead::isKnownDead(BasicBlock *).
+  bool isKnownDead(const BasicBlock *BB) const override { return false; }
+
+  /// See AAIsDead::isAssumedDead(Instruction *I).
+  bool isAssumedDead(const Instruction *I) const override {
+    return I == getCtxI() && isAssumedDead();
+  }
+
+  /// See AAIsDead::isKnownDead(Instruction *I).
+  bool isKnownDead(const Instruction *I) const override {
+    return I == getCtxI() && getKnown();
+  }
+
+  /// See AbstractAttribute::getAsStr().
+  const std::string getAsStr() const override {
+    return isAssumedDead() ? "assumed-dead" : "assumed-live";
+  }
+};
+
+struct AAIsDeadFloating : public AAIsDeadValueImpl {
+  AAIsDeadFloating(const IRPosition &IRP) : AAIsDeadValueImpl(IRP) {}
+
+  /// See AbstractAttribute::initialize(...).
+  void initialize(Attributor &A) override {
+    if (Instruction *I = dyn_cast<Instruction>(&getAssociatedValue()))
+      if (!wouldInstructionBeTriviallyDead(I))
+        indicatePessimisticFixpoint();
+    if (isa<UndefValue>(getAssociatedValue()))
+      indicatePessimisticFixpoint();
+  }
+
+  /// See AbstractAttribute::updateImpl(...).
+  ChangeStatus updateImpl(Attributor &A) override {
+    auto UsePred = [&](const Use &U, bool &Follow) {
+      Instruction *UserI = cast<Instruction>(U.getUser());
+      if (CallSite CS = CallSite(UserI)) {
+        if (!CS.isArgOperand(&U))
+          return false;
+        const IRPosition &CSArgPos =
+            IRPosition::callsite_argument(CS, CS.getArgumentNo(&U));
+        const auto &CSArgIsDead = A.getAAFor<AAIsDead>(*this, CSArgPos);
+        return CSArgIsDead.isAssumedDead();
+      }
+      if (ReturnInst *RI = dyn_cast<ReturnInst>(UserI)) {
+        const IRPosition &RetPos = IRPosition::returned(*RI->getFunction());
+        const auto &RetIsDeadAA = A.getAAFor<AAIsDead>(*this, RetPos);
+        return RetIsDeadAA.isAssumedDead();
+      }
+      Follow = true;
+      return wouldInstructionBeTriviallyDead(UserI);
+    };
+
+    if (!A.checkForAllUses(UsePred, *this, getAssociatedValue()))
+      return indicatePessimisticFixpoint();
+    return ChangeStatus::UNCHANGED;
+  }
+
+  /// See AbstractAttribute::manifest(...).
+  ChangeStatus manifest(Attributor &A) override {
+    Value &V = getAssociatedValue();
+    if (auto *I = dyn_cast<Instruction>(&V))
+      if (wouldInstructionBeTriviallyDead(I)) {
+          A.deleteAfterManifest(*I);
+          return ChangeStatus::CHANGED;
+      }
+
+    if (V.use_empty())
+      return ChangeStatus::UNCHANGED;
+
+    UndefValue &UV = *UndefValue::get(V.getType());
+    bool AnyChange = false;
+    for (Use &U : V.uses())
+      AnyChange |= A.changeUseAfterManifest(U, UV);
+    return AnyChange ? ChangeStatus::CHANGED : ChangeStatus::UNCHANGED;
+  }
+
+  /// See AbstractAttribute::trackStatistics()
+  void trackStatistics() const override {
+    STATS_DECLTRACK_FLOATING_ATTR(IsDead)
+  }
+};
+
+struct AAIsDeadArgument : public AAIsDeadFloating {
+  AAIsDeadArgument(const IRPosition &IRP) : AAIsDeadFloating(IRP) {}
+
+  /// See AbstractAttribute::initialize(...).
+  void initialize(Attributor &A) override {
+    if (!getAssociatedFunction()->hasExactDefinition())
+      indicatePessimisticFixpoint();
+  }
+
+  /// See AbstractAttribute::trackStatistics()
+  void trackStatistics() const override { STATS_DECLTRACK_ARG_ATTR(IsDead) }
+};
+
+struct AAIsDeadCallSiteArgument : public AAIsDeadValueImpl {
+  AAIsDeadCallSiteArgument(const IRPosition &IRP) : AAIsDeadValueImpl(IRP) {}
+
+  /// See AbstractAttribute::initialize(...).
+  void initialize(Attributor &A) override {
+    if (isa<UndefValue>(getAssociatedValue()))
+      indicatePessimisticFixpoint();
+  }
+
+  /// See AbstractAttribute::updateImpl(...).
+  ChangeStatus updateImpl(Attributor &A) override {
+    // TODO: Once we have call site specific value information we can provide
+    //       call site specific liveness information and then it makes
+    //       sense to specialize attributes for call sites arguments instead of
+    //       redirecting requests to the callee argument.
+    Argument *Arg = getAssociatedArgument();
+    if (!Arg)
+      return indicatePessimisticFixpoint();
+    const IRPosition &ArgPos = IRPosition::argument(*Arg);
+    auto &ArgAA = A.getAAFor<AAIsDead>(*this, ArgPos);
+    return clampStateAndIndicateChange(
+        getState(), static_cast<const AAIsDead::StateType &>(ArgAA.getState()));
+  }
+
+  /// See AbstractAttribute::manifest(...).
+  ChangeStatus manifest(Attributor &A) override {
+    CallBase &CB = cast<CallBase>(getAnchorValue());
+    Use &U = CB.getArgOperandUse(getArgNo());
+    assert(!isa<UndefValue>(U.get()) &&
+           "Expected undef values to be filtered out!");
+    UndefValue &UV = *UndefValue::get(U->getType());
+    if (A.changeUseAfterManifest(U, UV))
+      return ChangeStatus::CHANGED;
+    return ChangeStatus::UNCHANGED;
+  }
+
+  /// See AbstractAttribute::trackStatistics()
+  void trackStatistics() const override { STATS_DECLTRACK_CSARG_ATTR(IsDead) }
+};
+
+struct AAIsDeadReturned : public AAIsDeadValueImpl {
+  AAIsDeadReturned(const IRPosition &IRP) : AAIsDeadValueImpl(IRP) {}
+
+  /// See AbstractAttribute::updateImpl(...).
+  ChangeStatus updateImpl(Attributor &A) override {
+
+    auto PredForCallSite = [&](AbstractCallSite ACS) {
+      if (ACS.isCallbackCall())
+        return false;
+      const IRPosition &CSRetPos =
+          IRPosition::callsite_returned(ACS.getCallSite());
+      const auto &RetIsDeadAA = A.getAAFor<AAIsDead>(*this, CSRetPos);
+      return RetIsDeadAA.isAssumedDead();
+    };
+
+    if (!A.checkForAllCallSites(PredForCallSite, *this, true))
+      return indicatePessimisticFixpoint();
+
+    return ChangeStatus::UNCHANGED;
+  }
+
+  /// See AbstractAttribute::manifest(...).
+  ChangeStatus manifest(Attributor &A) override {
+    // TODO: Rewrite the signature to return void?
+    bool AnyChange = false;
+    UndefValue &UV = *UndefValue::get(getAssociatedFunction()->getReturnType());
+    auto RetInstPred = [&](Instruction &I) {
+      ReturnInst &RI = cast<ReturnInst>(I);
+      if (!isa<UndefValue>(RI.getReturnValue()))
+        AnyChange |= A.changeUseAfterManifest(RI.getOperandUse(0), UV);
+      return true;
+    };
+    A.checkForAllInstructions(RetInstPred, *this, {Instruction::Ret});
+    return AnyChange ? ChangeStatus::CHANGED : ChangeStatus::UNCHANGED;
+  }
+
+  /// See AbstractAttribute::trackStatistics()
+  void trackStatistics() const override { STATS_DECLTRACK_FNRET_ATTR(IsDead) }
+};
+
+struct AAIsDeadCallSiteReturned : public AAIsDeadFloating {
+  AAIsDeadCallSiteReturned(const IRPosition &IRP) : AAIsDeadFloating(IRP) {}
+
+  /// See AbstractAttribute::initialize(...).
+  void initialize(Attributor &A) override {}
+
+  /// See AbstractAttribute::trackStatistics()
+  void trackStatistics() const override { STATS_DECLTRACK_CSRET_ATTR(IsDead) }
+};
+
+struct AAIsDeadFunction : public AAIsDead {
+  AAIsDeadFunction(const IRPosition &IRP) : AAIsDead(IRP) {}
+
+  /// See AbstractAttribute::initialize(...).
   void initialize(Attributor &A) override {
     const Function *F = getAssociatedFunction();
     if (F && !F->isDeclaration())
@@ -2231,6 +2426,12 @@ struct AAIsDeadImpl : public AAIsDead {
   /// See AbstractAttribute::updateImpl(...).
   ChangeStatus updateImpl(Attributor &A) override;
 
+  /// See AbstractAttribute::trackStatistics()
+  void trackStatistics() const override {}
+
+  /// Returns true if the function is assumed dead.
+  bool isAssumedDead() const override { return false; }
+
   /// See AAIsDead::isAssumedDead(BasicBlock *).
   bool isAssumedDead(const BasicBlock *BB) const override {
     assert(BB->getParent() == getAssociatedFunction() &&
@@ -2303,18 +2504,7 @@ struct AAIsDeadImpl : public AAIsDead {
   SmallSetVector<const Instruction *, 4> NoReturnCalls;
 };
 
-struct AAIsDeadFunction final : public AAIsDeadImpl {
-  AAIsDeadFunction(const IRPosition &IRP) : AAIsDeadImpl(IRP) {}
-
-  /// See AbstractAttribute::trackStatistics()
-  void trackStatistics() const override {
-    STATS_DECL(PartiallyDeadBlocks, Function,
-               "Number of basic blocks classified as partially dead");
-    BUILD_STAT_NAME(PartiallyDeadBlocks, Function) += NoReturnCalls.size();
-  }
-};
-
-bool AAIsDeadImpl::isAfterNoReturn(const Instruction *I) const {
+bool AAIsDeadFunction::isAfterNoReturn(const Instruction *I) const {
   const Instruction *PrevI = I->getPrevNode();
   while (PrevI) {
     if (NoReturnCalls.count(PrevI))
@@ -2324,8 +2514,8 @@ bool AAIsDeadImpl::isAfterNoReturn(const Instruction *I) const {
   return false;
 }
 
-const Instruction *AAIsDeadImpl::findNextNoReturn(Attributor &A,
-                                                  const Instruction *I) {
+const Instruction *AAIsDeadFunction::findNextNoReturn(Attributor &A,
+                                                      const Instruction *I) {
   const BasicBlock *BB = I->getParent();
   const Function &F = *BB->getParent();
 
@@ -2374,7 +2564,7 @@ const Instruction *AAIsDeadImpl::findNextNoReturn(Attributor &A,
   return nullptr;
 }
 
-ChangeStatus AAIsDeadImpl::updateImpl(Attributor &A) {
+ChangeStatus AAIsDeadFunction::updateImpl(Attributor &A) {
   ChangeStatus Status = ChangeStatus::UNCHANGED;
 
   // Temporary collection to iterate over existing noreturn instructions. This
@@ -2423,8 +2613,8 @@ ChangeStatus AAIsDeadImpl::updateImpl(Attributor &A) {
 }
 
 /// Liveness information for a call sites.
-struct AAIsDeadCallSite final : AAIsDeadImpl {
-  AAIsDeadCallSite(const IRPosition &IRP) : AAIsDeadImpl(IRP) {}
+struct AAIsDeadCallSite final : AAIsDeadFunction {
+  AAIsDeadCallSite(const IRPosition &IRP) : AAIsDeadFunction(IRP) {}
 
   /// See AbstractAttribute::initialize(...).
   void initialize(Attributor &A) override {
@@ -4249,6 +4439,8 @@ bool Attributor::isAssumedDead(const AbstractAttribute &AA,
   if (!CtxI)
     return false;
 
+  // TODO: Find a good way to utilize fine and coarse grained liveness
+  // information.
   if (!LivenessAA)
     LivenessAA =
         &getAAFor<AAIsDead>(AA, IRPosition::function(*CtxI->getFunction()),
@@ -4267,6 +4459,58 @@ bool Attributor::isAssumedDead(const AbstractAttribute &AA,
   return true;
 }
 
+bool Attributor::checkForAllUses(
+    const function_ref<bool(const Use &, bool &)> &Pred,
+    const AbstractAttribute &QueryingAA, const Value &V) {
+  const IRPosition &IRP = QueryingAA.getIRPosition();
+  SmallVector<const Use *, 16> Worklist;
+  SmallPtrSet<const Use *, 16> Visited;
+
+  for (const Use &U : V.uses())
+    Worklist.push_back(&U);
+
+  LLVM_DEBUG(dbgs() << "[Attributor] Got " << Worklist.size()
+                    << " initial uses to check\n");
+
+  if (Worklist.empty())
+    return true;
+
+  bool AnyDead = false;
+  const Function *ScopeFn = IRP.getAnchorScope();
+  const auto *LivenessAA =
+      ScopeFn ? &getAAFor<AAIsDead>(QueryingAA, IRPosition::function(*ScopeFn),
+                                    /* TrackDependence */ false)
+              : nullptr;
+
+  while (!Worklist.empty()) {
+    const Use *U = Worklist.pop_back_val();
+    if (!Visited.insert(U).second)
+      continue;
+    LLVM_DEBUG(dbgs() << "[Attributor] Check use: " << **U << "\n");
+    if (Instruction *UserI = dyn_cast<Instruction>(U->getUser()))
+      if (LivenessAA && LivenessAA->isAssumedDead(UserI)) {
+        LLVM_DEBUG(dbgs() << "[Attributor] Dead user: " << *UserI << ": "
+                          << static_cast<const AbstractAttribute &>(*LivenessAA)
+                          << "\n");
+        AnyDead = true;
+        continue;
+      }
+
+    bool Follow = false;
+    if (!Pred(*U, Follow))
+      return false;
+    if (!Follow)
+      continue;
+    for (const Use &UU : U->getUser()->uses())
+      Worklist.push_back(&UU);
+  }
+
+  if (AnyDead)
+    recordDependence(*LivenessAA, QueryingAA);
+
+  return true;
+}
+
 bool Attributor::checkForAllCallSites(
     const function_ref<bool(AbstractCallSite)> &Pred,
     const AbstractAttribute &QueryingAA, bool RequireAllCallSites) {
@@ -4633,13 +4877,48 @@ ChangeStatus Attributor::run(Module &M) {
     LLVM_DEBUG(dbgs() << "\n[Attributor] Delete at least "
                       << ToBeDeletedFunctions.size() << " functions and "
                       << ToBeDeletedBlocks.size() << " blocks and "
-                      << ToBeDeletedInsts.size() << " instructions\n");
+                      << ToBeDeletedInsts.size() << " instructions and "
+                      << ToBeChangedUses.size() << " uses\n");
+
+    SmallVector<Instruction *, 32> DeadInsts;
+    SmallVector<Instruction *, 32> TerminatorsToFold;
+    SmallVector<Instruction *, 32> UnreachablesToInsert;
+
+    for (auto &It : ToBeChangedUses) {
+      Use *U = It.first;
+      Value *NewV = It.second;
+      Value *OldV = U->get();
+      LLVM_DEBUG(dbgs() << "Use " << *NewV << " in " << *U->getUser()
+                        << " instead of " << *OldV << "\n");
+      U->set(NewV);
+      if (Instruction *I = dyn_cast<Instruction>(OldV))
+        if (!isa<PHINode>(I) && !ToBeDeletedInsts.count(I) && isInstructionTriviallyDead(I)) {
+          DeadInsts.push_back(I);
+        }
+      if (isa<Constant>(NewV) && isa<BranchInst>(U->getUser())) {
+        Instruction *UserI = cast<Instruction>(U->getUser());
+        if (isa<UndefValue>(NewV)) {
+          UnreachablesToInsert.push_back(UserI);
+        } else {
+          TerminatorsToFold.push_back(UserI);
+        }
+      }
+    }
+    for (Instruction *I : UnreachablesToInsert)
+      changeToUnreachable(I, /* UseLLVMTrap */ false);
+    for (Instruction *I : TerminatorsToFold)
+      ConstantFoldTerminator(I->getParent());
+
     for (Instruction *I : ToBeDeletedInsts) {
-      if (!I->use_empty())
-        I->replaceAllUsesWith(UndefValue::get(I->getType()));
-      I->eraseFromParent();
+      I->replaceAllUsesWith(UndefValue::get(I->getType()));
+      if (!isa<PHINode>(I) && isInstructionTriviallyDead(I))
+        DeadInsts.push_back(I);
+      else
+        I->eraseFromParent();
     }
 
+    RecursivelyDeleteTriviallyDeadInstructions(DeadInsts);
+
     if (unsigned NumDeadBlocks = ToBeDeletedBlocks.size()) {
       SmallVector<BasicBlock *, 8> ToBeDeletedBBs;
       ToBeDeletedBBs.reserve(NumDeadBlocks);
@@ -4676,14 +4955,12 @@ ChangeStatus Attributor::run(Module &M) {
         if (!F)
           continue;
 
-        const auto *LivenessAA =
-            lookupAAFor<AAIsDead>(IRPosition::function(*F));
-        if (LivenessAA &&
-            !checkForAllCallSites([](AbstractCallSite ACS) { return false; },
-                                  *LivenessAA, true))
+        if (!checkForAllCallSites([](AbstractCallSite ACS) { return false; },
+                                  *F, true, nullptr))
           continue;
 
         STATS_TRACK(AAIsDead, Function);
+        ToBeDeletedFunctions.insert(F);
         F->replaceAllUsesWith(UndefValue::get(F->getType()));
         F->eraseFromParent();
         InternalFns[u] = nullptr;
@@ -4800,6 +5077,9 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) {
 
     IRPosition RetPos = IRPosition::returned(F);
 
+    // Every returned value might be dead.
+    getOrCreateAAFor<AAIsDead>(RetPos);
+
     // Every function might be simplified.
     getOrCreateAAFor<AAValueSimplify>(RetPos);
 
@@ -4850,11 +5130,21 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) {
 
   auto CallSitePred = [&](Instruction &I) -> bool {
     CallSite CS(&I);
-    if (CS.getCalledFunction()) {
-      for (int i = 0, e = CS.getCalledFunction()->arg_size(); i < e; i++) {
+    if (Function *Callee = CS.getCalledFunction()) {
+      if (!Callee->getReturnType()->isVoidTy()) {
+        IRPosition CSRetPos = IRPosition::callsite_returned(CS);
+
+        // Call site return values might be dead.
+        getOrCreateAAFor<AAIsDead>(CSRetPos);
+      }
+
+      for (int i = 0, e = Callee->arg_size(); i < e; i++) {
 
         IRPosition CSArgPos = IRPosition::callsite_argument(CS, i);
 
+        // Every call site argument might be dead.
+        getOrCreateAAFor<AAIsDead>(CSArgPos);
+
         // Call site argument might be simplified.
         getOrCreateAAFor<AAValueSimplify>(CSArgPos);
 
@@ -5156,7 +5446,6 @@ CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoFree)
 CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoRecurse)
 CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAWillReturn)
 CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoReturn)
-CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAIsDead)
 CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAReturnedValues)
 
 CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANonNull)
@@ -5166,6 +5455,7 @@ CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAAlign)
 CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoCapture)
 
 CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAValueSimplify)
+CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAIsDead)
 
 CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAHeapToStack)
 

diff  --git a/llvm/test/Transforms/FunctionAttrs/align.ll b/llvm/test/Transforms/FunctionAttrs/align.ll
index f986d31af0ab..f4cb54289cad 100644
--- a/llvm/test/Transforms/FunctionAttrs/align.ll
+++ b/llvm/test/Transforms/FunctionAttrs/align.ll
@@ -1,3 +1,4 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
 ; RUN: opt -attributor -attributor-manifest-internal -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=6 -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR
 
 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
@@ -83,14 +84,11 @@ define i32* @test6_2() #0 {
 
 ; Function Attrs: nounwind readnone ssp uwtable
 define internal i8* @f1(i8* readnone %0) local_unnamed_addr #0 {
-; ATTRIBUTOR: define internal nonnull align 8 dereferenceable(1) i8* @f1(i8* nonnull readnone align 8 dereferenceable(1) "no-capture-maybe-returned" %0)
   %2 = icmp eq i8* %0, null
   br i1 %2, label %3, label %5
 
 ; <label>:3:                                      ; preds = %1
-; ATTRIBUTOR: %4 = tail call align 8 i8* @f2(i8* nonnull align 8 dereferenceable(1) @a1)
   %4 = tail call i8* @f2(i8* nonnull @a1)
-; ATTRIBUTOR: %l = load i8, i8* %4, align 8
   %l = load i8, i8* %4
   br label %5
 
@@ -101,18 +99,15 @@ define internal i8* @f1(i8* readnone %0) local_unnamed_addr #0 {
 
 ; Function Attrs: nounwind readnone ssp uwtable
 define internal i8* @f2(i8* readnone %0) local_unnamed_addr #0 {
-; ATTRIBUTOR: define internal nonnull align 8 dereferenceable(1) i8* @f2(i8* nonnull readnone align 8 dereferenceable(1) "no-capture-maybe-returned" %0)
   %2 = icmp eq i8* %0, null
   br i1 %2, label %5, label %3
 
 ; <label>:3:                                      ; preds = %1
 
-; ATTRIBUTOR: %4 = tail call i8* @f1(i8* nonnull align 8 dereferenceable(1) "no-capture-maybe-returned" @a1)
   %4 = tail call i8* @f1(i8* nonnull %0)
   br label %7
 
 ; <label>:5:                                      ; preds = %1
-; ATTRIBUTOR: %6 = tail call i8* @f3(i8* nonnull align 16 dereferenceable(1) @a2)
   %6 = tail call i8* @f3(i8* nonnull @a2)
   br label %7
 
@@ -123,12 +118,10 @@ define internal i8* @f2(i8* readnone %0) local_unnamed_addr #0 {
 
 ; Function Attrs: nounwind readnone ssp uwtable
 define internal i8* @f3(i8* readnone %0) local_unnamed_addr #0 {
-; ATTRIBUTOR: define internal nonnull align 8 dereferenceable(1) i8* @f3(i8* nocapture nonnull readnone align 16 dereferenceable(1) %0)
   %2 = icmp eq i8* %0, null
   br i1 %2, label %3, label %5
 
 ; <label>:3:                                      ; preds = %1
-; ATTRIBUTOR: %4 = tail call i8* @f1(i8* nonnull align 16 dereferenceable(1) @a2)
   %4 = tail call i8* @f1(i8* nonnull @a2)
   br label %5
 
@@ -139,12 +132,116 @@ define internal i8* @f3(i8* readnone %0) local_unnamed_addr #0 {
 
 ; TEST 7
 ; Better than IR information
-; ATTRIBUTOR: define align 32 i32* @test7(i32* readnone returned align 32 "no-capture-maybe-returned" %p)
 define align 4 i32* @test7(i32* align 32 %p) #0 {
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test7
+; ATTRIBUTOR-SAME: (i32* readnone returned align 32 "no-capture-maybe-returned" [[P:%.*]])
+; ATTRIBUTOR-NEXT:    ret i32* [[P:%.*]]
+;
   tail call i8* @f1(i8* align 8 dereferenceable(1) @a1)
   ret i32* %p
 }
 
+; TEST 7b
+; Function Attrs: nounwind readnone ssp uwtable
+define internal i8* @f1b(i8* readnone %0) local_unnamed_addr #0 {
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@f1b
+; ATTRIBUTOR-SAME: (i8* nonnull readnone align 8 dereferenceable(1) "no-capture-maybe-returned" [[TMP0:%.*]]) local_unnamed_addr
+; ATTRIBUTOR-NEXT:    [[TMP2:%.*]] = icmp eq i8* [[TMP0:%.*]], null
+; ATTRIBUTOR-NEXT:    br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]]
+; ATTRIBUTOR:       3:
+; ATTRIBUTOR-NEXT:    [[TMP4:%.*]] = tail call align 8 i8* @f2b(i8* nonnull align 8 dereferenceable(1) @a1)
+; ATTRIBUTOR-NEXT:    [[L:%.*]] = load i8, i8* [[TMP4]], align 8
+; ATTRIBUTOR-NEXT:    store i8 [[L]], i8* @a1, align 8
+; ATTRIBUTOR-NEXT:    br label [[TMP5]]
+; ATTRIBUTOR:       5:
+; ATTRIBUTOR-NEXT:    [[TMP6:%.*]] = phi i8* [ [[TMP4]], [[TMP3]] ], [ [[TMP0]], [[TMP1:%.*]] ]
+; ATTRIBUTOR-NEXT:    ret i8* [[TMP6]]
+;
+  %2 = icmp eq i8* %0, null
+  br i1 %2, label %3, label %5
+
+; <label>:3:                                      ; preds = %1
+  %4 = tail call i8* @f2b(i8* nonnull @a1)
+  %l = load i8, i8* %4
+  store i8 %l, i8* @a1
+  br label %5
+
+; <label>:5:                                      ; preds = %1, %3
+  %6 = phi i8* [ %4, %3 ], [ %0, %1 ]
+  ret i8* %6
+}
+
+; Function Attrs: nounwind readnone ssp uwtable
+define internal i8* @f2b(i8* readnone %0) local_unnamed_addr #0 {
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@f2b
+; ATTRIBUTOR-SAME: (i8* nonnull readnone align 8 dereferenceable(1) "no-capture-maybe-returned" [[TMP0:%.*]]) local_unnamed_addr
+; ATTRIBUTOR-NEXT:    [[TMP2:%.*]] = icmp eq i8* @a1, null
+; ATTRIBUTOR-NEXT:    br i1 [[TMP2]], label [[TMP5:%.*]], label [[TMP3:%.*]]
+; ATTRIBUTOR:       3:
+; ATTRIBUTOR-NEXT:    [[TMP4:%.*]] = tail call i8* @f1b(i8* nonnull align 8 dereferenceable(1) "no-capture-maybe-returned" @a1)
+; ATTRIBUTOR-NEXT:    br label [[TMP7:%.*]]
+; ATTRIBUTOR:       5:
+; ATTRIBUTOR-NEXT:    [[TMP6:%.*]] = tail call i8* @f3b(i8* nonnull align 16 dereferenceable(1) @a2)
+; ATTRIBUTOR-NEXT:    br label [[TMP7]]
+; ATTRIBUTOR:       7:
+; ATTRIBUTOR-NEXT:    [[TMP8:%.*]] = phi i8* [ [[TMP4]], [[TMP3]] ], [ [[TMP6]], [[TMP5]] ]
+; ATTRIBUTOR-NEXT:    ret i8* [[TMP8]]
+;
+  %2 = icmp eq i8* %0, null
+  br i1 %2, label %5, label %3
+
+; <label>:3:                                      ; preds = %1
+
+  %4 = tail call i8* @f1b(i8* nonnull %0)
+  br label %7
+
+; <label>:5:                                      ; preds = %1
+  %6 = tail call i8* @f3b(i8* nonnull @a2)
+  br label %7
+
+; <label>:7:                                      ; preds = %5, %3
+  %8 = phi i8* [ %4, %3 ], [ %6, %5 ]
+  ret i8* %8
+}
+
+; Function Attrs: nounwind readnone ssp uwtable
+define internal i8* @f3b(i8* readnone %0) local_unnamed_addr #0 {
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@f3b
+; ATTRIBUTOR-SAME: (i8* nocapture nonnull readnone align 16 dereferenceable(1) [[TMP0:%.*]]) local_unnamed_addr
+; ATTRIBUTOR-NEXT:    [[TMP2:%.*]] = icmp eq i8* @a2, null
+; ATTRIBUTOR-NEXT:    br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]]
+; ATTRIBUTOR:       3:
+; ATTRIBUTOR-NEXT:    [[TMP4:%.*]] = tail call i8* @f1b(i8* nonnull align 16 dereferenceable(1) @a2)
+; ATTRIBUTOR-NEXT:    br label [[TMP5]]
+; ATTRIBUTOR:       5:
+; ATTRIBUTOR-NEXT:    [[TMP6:%.*]] = phi i8* [ [[TMP4]], [[TMP3]] ], [ @a1, [[TMP1:%.*]] ]
+; ATTRIBUTOR-NEXT:    ret i8* [[TMP6]]
+;
+  %2 = icmp eq i8* %0, null
+  br i1 %2, label %3, label %5
+
+; <label>:3:                                      ; preds = %1
+  %4 = tail call i8* @f1b(i8* nonnull @a2)
+  br label %5
+
+; <label>:5:                                      ; preds = %1, %3
+  %6 = phi i8* [ %4, %3 ], [ @a1, %1 ]
+  ret i8* %6
+}
+
+define align 4 i32* @test7b(i32* align 32 %p) #0 {
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test7b
+; ATTRIBUTOR-SAME: (i32* readnone returned align 32 "no-capture-maybe-returned" [[P:%.*]])
+; ATTRIBUTOR-NEXT:    [[TMP1:%.*]] = tail call i8* @f1b(i8* nonnull align 8 dereferenceable(1) @a1)
+; ATTRIBUTOR-NEXT:    ret i32* [[P:%.*]]
+;
+  tail call i8* @f1b(i8* align 8 dereferenceable(1) @a1)
+  ret i32* %p
+}
+
 
 ; TEST 8
 define void @test8_helper() {
@@ -161,8 +258,12 @@ define void @test8_helper() {
   ret void
 }
 
+declare void @user_i32_ptr(i32*) readnone nounwind
 define internal void @test8(i32* %a, i32* %b, i32* %c) {
 ; ATTRIBUTOR: define internal void @test8(i32* nocapture readnone align 4 %a, i32* nocapture readnone align 4 %b, i32* nocapture readnone %c)
+  call void @user_i32_ptr(i32* %a)
+  call void @user_i32_ptr(i32* %b)
+  call void @user_i32_ptr(i32* %c)
   ret void
 }
 

diff  --git a/llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll b/llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
index 7afaab6637e4..cb598180bc2d 100644
--- a/llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
+++ b/llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
@@ -90,8 +90,8 @@ entry:
 define i32* @srec16(i32* %a) #0 {
 entry:
   %call = call i32* @srec16(i32* %a)
-; CHECK:      %call
-; CHECK-NEXT: unreachable
+; CHECK-NOT:      %call
+; CHECK: unreachable
   %call1 = call i32* @srec16(i32* %call)
   %call2 = call i32* @srec16(i32* %call1)
   %call3 = call i32* @srec16(i32* %call2)

diff  --git a/llvm/test/Transforms/FunctionAttrs/arg_returned.ll b/llvm/test/Transforms/FunctionAttrs/arg_returned.ll
index d927cdf79278..514438eaa243 100644
--- a/llvm/test/Transforms/FunctionAttrs/arg_returned.ll
+++ b/llvm/test/Transforms/FunctionAttrs/arg_returned.ll
@@ -253,8 +253,8 @@ return:                                           ; preds = %cond.end, %if.then3
 ; }
 ;
 ; FNATTR:  define i32* @rt0(i32* readonly %a)
-; BOTH: Function Attrs: nofree noinline noreturn nosync nounwind readonly uwtable
-; BOTH-NEXT:    define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @rt0(i32* nocapture nonnull readonly dereferenceable(4) %a)
+; BOTH:      Function Attrs: nofree noinline norecurse noreturn nosync nounwind readonly uwtable
+; BOTH-NEXT: define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @rt0(i32* nocapture nonnull readonly dereferenceable(4) %a)
 define i32* @rt0(i32* %a) #0 {
 entry:
   %v = load i32, i32* %a, align 4
@@ -271,7 +271,7 @@ entry:
 ; }
 ;
 ; FNATTR:  define noalias i32* @rt1(i32* nocapture readonly %a)
-; BOTH: Function Attrs: nofree noinline noreturn nosync nounwind readonly uwtable
+; BOTH: Function Attrs: nofree noinline norecurse noreturn nosync nounwind readonly uwtable
 ; BOTH-NEXT:    define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @rt1(i32* nocapture nonnull readonly dereferenceable(4) %a)
 define i32* @rt1(i32* %a) #0 {
 entry:
@@ -837,15 +837,3 @@ define i32* @dont_use_const() #0 {
 }
 
 attributes #0 = { noinline nounwind uwtable }
-
-; BOTH-NOT: attributes #
-; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noinline norecurse nosync nounwind readnone uwtable willreturn }
-; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noinline nosync nounwind readnone uwtable }
-; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noinline noreturn nosync nounwind readonly uwtable }
-; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nounwind uwtable }
-; BOTH-DAG: attributes #{{[0-9]*}} = { noreturn }
-; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noinline norecurse nosync nounwind readnone uwtable }
-; BOTH-DAG: attributes #{{[0-9]*}} = { nofree nosync readnone willreturn }
-; BOTH-DAG: attributes #{{[0-9]*}} = { nofree nosync readnone }
-; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noreturn nosync readonly }
-; BOTH-NOT: attributes #

diff  --git a/llvm/test/Transforms/FunctionAttrs/liveness.ll b/llvm/test/Transforms/FunctionAttrs/liveness.ll
index f029f9476b75..cde910f2c2ed 100644
--- a/llvm/test/Transforms/FunctionAttrs/liveness.ll
+++ b/llvm/test/Transforms/FunctionAttrs/liveness.ll
@@ -1,4 +1,4 @@
-; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=3 -S < %s | FileCheck %s
+; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=5 -S < %s | FileCheck %s
 
 declare void @no_return_call() nofree noreturn nounwind readnone
 
@@ -39,8 +39,7 @@ define i32 @volatile_load(i32*) norecurse nounwind uwtable {
   ret i32 %2
 }
 
-; CHECK: Function Attrs: nofree norecurse nosync nounwind readonly uwtable willreturn
-; CHECK-NEXT: define internal i32 @internal_load(i32* nocapture nonnull readonly dereferenceable(4) %0)
+; CHECK-NOT: internal_load
 define internal i32 @internal_load(i32*) norecurse nounwind uwtable {
   %2 = load i32, i32* %0, align 4
   ret i32 %2
@@ -52,7 +51,6 @@ define internal i32 @internal_load(i32*) norecurse nounwind uwtable {
 define i32 @first_block_no_return(i32 %a, i32* nonnull %ptr1, i32* %ptr2) #0 {
 entry:
   call i32 @internal_load(i32* %ptr1)
-  ; CHECK: call i32 @internal_load(i32* nocapture nonnull readonly %ptr1)
   call void @no_return_call()
   ; CHECK: call void @no_return_call()
   ; CHECK-NEXT: unreachable
@@ -716,3 +714,36 @@ lp2:
 live_with_dead_entry:
   ret void
 }
+
+; CHECK: define internal void @useless_arg_sink(i32* nocapture readnone %a)
+define internal void @useless_arg_sink(i32* %a) {
+  ret void
+}
+
+; CHECK: define internal void @useless_arg_almost_sink(i32* nocapture readnone %a)
+define internal void @useless_arg_almost_sink(i32* %a) {
+; CHECK: call void @useless_arg_sink(i32* undef)
+  call void @useless_arg_sink(i32* %a)
+  ret void
+}
+
+; Check we do not annotate the function interface of this weak function.
+; CHECK: define weak_odr void @useless_arg_ext(i32* %a)
+define weak_odr void @useless_arg_ext(i32* %a) {
+; CHECK: call void @useless_arg_almost_sink(i32* undef)
+  call void @useless_arg_almost_sink(i32* %a)
+  ret void
+}
+
+; CHECK: define internal void @useless_arg_ext_int(i32* %a)
+define internal void @useless_arg_ext_int(i32* %a) {
+; CHECK: call void @useless_arg_ext(i32* %a)
+  call void @useless_arg_ext(i32* %a)
+  ret void
+}
+
+define void @useless_arg_ext_int_ext(i32* %a) {
+; CHECK: call void @useless_arg_ext_int(i32* %a)
+  call void @useless_arg_ext_int(i32* %a)
+  ret void
+}

diff  --git a/llvm/test/Transforms/FunctionAttrs/misc.ll b/llvm/test/Transforms/FunctionAttrs/misc.ll
index 96ceb8743fdf..ea4a5046fd09 100644
--- a/llvm/test/Transforms/FunctionAttrs/misc.ll
+++ b/llvm/test/Transforms/FunctionAttrs/misc.ll
@@ -1,20 +1,73 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
 ; RUN: opt -S -attributor -attributor-disable=false < %s | FileCheck %s
+; RUN: opt -S -aa-pipeline='basic-aa' -passes=attributor -attributor-disable=false < %s | FileCheck %s
+;
+; Mostly check we do not crash on these uses
 
-define void @external() {
+define internal void @internal(void (i8*)* %fp) {
+; CHECK-LABEL: define {{[^@]+}}@internal
+; CHECK-SAME: (void (i8*)* [[FP:%.*]])
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[TMP:%.*]] = bitcast i32* [[A]] to i8*
+; CHECK-NEXT:    call void @foo(i32* nocapture nonnull align 4 dereferenceable(4) undef)
+; CHECK-NEXT:    call void [[FP:%.*]](i8* bitcast (void (i32*)* @foo to i8*))
+; CHECK-NEXT:    call void @callback1(void (i32*)* nonnull @foo)
+; CHECK-NEXT:    call void @callback2(void (i8*)* nonnull bitcast (void (i32*)* @foo to void (i8*)*))
+; CHECK-NEXT:    call void @callback2(void (i8*)* [[FP]])
+; CHECK-NEXT:    [[TMP1:%.*]] = bitcast i32* [[A]] to i8*
+; CHECK-NEXT:    call void [[FP]](i8* [[TMP1]])
+; CHECK-NEXT:    ret void
+;
 entry:
   %a = alloca i32, align 4
   %tmp = bitcast i32* %a to i8*
   call void @foo(i32* nonnull %a)
-; Check we do not crash on these uses
-; CHECK: call void @callback1(void (i32*)* nonnull @foo)
+  call void %fp(i8* bitcast (void (i32*)* @foo to i8*))
   call void @callback1(void (i32*)* nonnull @foo)
-; CHECK: call void @callback2(void (i8*)* nonnull bitcast (void (i32*)* @foo to void (i8*)*))
   call void @callback2(void (i8*)* bitcast (void (i32*)* @foo to void (i8*)*))
+  call void @callback2(void (i8*)* %fp)
   %tmp1 = bitcast i32* %a to i8*
+  call void %fp(i8* %tmp1)
+  ret void
+}
+
+define void @external(void (i8*)* %fp) {
+; CHECK-LABEL: define {{[^@]+}}@external
+; CHECK-SAME: (void (i8*)* [[FP:%.*]])
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[TMP:%.*]] = bitcast i32* [[A]] to i8*
+; CHECK-NEXT:    call void @foo(i32* nocapture nonnull align 4 dereferenceable(4) undef)
+; CHECK-NEXT:    call void @callback1(void (i32*)* nonnull @foo)
+; CHECK-NEXT:    call void @callback2(void (i8*)* nonnull bitcast (void (i32*)* @foo to void (i8*)*))
+; CHECK-NEXT:    call void @callback2(void (i8*)* [[FP:%.*]])
+; CHECK-NEXT:    call void [[FP]](i8* bitcast (void (i32*)* @foo to i8*))
+; CHECK-NEXT:    [[TMP1:%.*]] = bitcast i32* [[A]] to i8*
+; CHECK-NEXT:    call void [[FP]](i8* [[TMP1]])
+; CHECK-NEXT:    call void @internal(void (i8*)* [[FP]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  %a = alloca i32, align 4
+  %tmp = bitcast i32* %a to i8*
+  call void @foo(i32* nonnull %a)
+  call void @callback1(void (i32*)* nonnull @foo)
+  call void @callback2(void (i8*)* bitcast (void (i32*)* @foo to void (i8*)*))
+  call void @callback2(void (i8*)* %fp)
+  call void %fp(i8* bitcast (void (i32*)* @foo to i8*))
+  %tmp1 = bitcast i32* %a to i8*
+  call void %fp(i8* %tmp1)
+  call void @internal(void (i8*)* %fp)
   ret void
 }
 
 define internal void @foo(i32* %a) {
+; CHECK-LABEL: define {{[^@]+}}@foo
+; CHECK-SAME: (i32* nocapture readnone [[A:%.*]])
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    ret void
+;
 entry:
   ret void
 }

diff  --git a/llvm/test/Transforms/FunctionAttrs/new_attributes.ll b/llvm/test/Transforms/FunctionAttrs/new_attributes.ll
index d2f6854bd3cd..aa71b64b9821 100644
--- a/llvm/test/Transforms/FunctionAttrs/new_attributes.ll
+++ b/llvm/test/Transforms/FunctionAttrs/new_attributes.ll
@@ -20,7 +20,7 @@ declare i32 @foo3()
 ; CHECK-NEXT:   %1 = call i32 @foo1()
 ; CHECK-NEXT:   %2 = call i32 @foo2()
 ; CHECK-NEXT:   %3 = call i32 @foo3()
-; CHECK-NEXT:   ret i32 1
+; CHECK-NEXT:   ret i32 undef
 ; CHECK-NEXT: }
 define internal i32 @bar() {
   %1 = call i32 @foo1()

diff  --git a/llvm/test/Transforms/FunctionAttrs/noalias_returned.ll b/llvm/test/Transforms/FunctionAttrs/noalias_returned.ll
index 8cb1ec2eb4eb..7c65a4350d08 100644
--- a/llvm/test/Transforms/FunctionAttrs/noalias_returned.ll
+++ b/llvm/test/Transforms/FunctionAttrs/noalias_returned.ll
@@ -168,6 +168,8 @@ define void @test9_helper(i8* %a, i8* %b) {
 
 declare void @test10_helper_1(i8* %a)
 define void @test10_helper_2(i8* noalias %a) {
+; CHECK:   tail call void @test10_helper_1(i8* %a)
+  tail call void @test10_helper_1(i8* %a)
   ret void
 }
 define void @test10(i8* noalias %a) {

diff  --git a/llvm/test/Transforms/FunctionAttrs/nonnull.ll b/llvm/test/Transforms/FunctionAttrs/nonnull.ll
index 657ef7152eb7..7e766da9d073 100644
--- a/llvm/test/Transforms/FunctionAttrs/nonnull.ll
+++ b/llvm/test/Transforms/FunctionAttrs/nonnull.ll
@@ -221,12 +221,12 @@ bb:
 
 define dso_local noalias i32* @f3(i32* %arg) {
 ; FIXME: missing nonnull. It should be nonnull @f3(i32* nonnull readonly %arg)
-; ATTRIBUTOR: define dso_local noalias i32* @f3(i32* nocapture readonly %arg)
+; ATTRIBUTOR: define dso_local noalias nonnull i32* @f3(i32* readonly %arg)
 bb:
 ; FIXME: missing nonnull. It should be @f1(i32* nonnull readonly %arg)
-; ATTRIBUTOR:   %tmp = call i32* @f1(i32* readonly %arg)
+; ATTRIBUTOR:   %tmp = call nonnull i32* @f1(i32* readonly %arg)
   %tmp = call i32* @f1(i32* %arg)
-  ret i32* null
+  ret i32* %tmp
 }
 
 ; TEST 15


        


More information about the llvm-commits mailing list