[llvm] e425a4c - Revert "[Attributor] Introduce AA[Intra/Inter]Reachability"

Mitch Phillips via llvm-commits llvm-commits at lists.llvm.org
Fri Dec 16 17:57:36 PST 2022


Author: Mitch Phillips
Date: 2022-12-16T17:56:48-08:00
New Revision: e425a4c45618fcfa8ffb13be4ddfaa5d28aa38f1

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

LOG: Revert "[Attributor] Introduce AA[Intra/Inter]Reachability"

This reverts commit fc21f2d7bae2e0be630470cc7ca9323ed5859892.

This patch broke the ASan buildbot. See
https://reviews.llvm.org/rGfc21f2d7bae2e0be630470cc7ca9323ed5859892 for
more information.

Added: 
    

Modified: 
    llvm/include/llvm/Transforms/IPO/Attributor.h
    llvm/lib/Transforms/IPO/Attributor.cpp
    llvm/lib/Transforms/IPO/AttributorAttributes.cpp
    llvm/test/Transforms/Attributor/ArgumentPromotion/alloca-as.ll
    llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll
    llvm/test/Transforms/Attributor/ArgumentPromotion/naked_functions.ll
    llvm/test/Transforms/Attributor/ArgumentPromotion/nonzero-address-spaces.ll
    llvm/test/Transforms/Attributor/ArgumentPromotion/pr27568.ll
    llvm/test/Transforms/Attributor/ArgumentPromotion/profile.ll
    llvm/test/Transforms/Attributor/ArgumentPromotion/variadic.ll
    llvm/test/Transforms/Attributor/IPConstantProp/naked-return.ll
    llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll
    llvm/test/Transforms/Attributor/cgscc_bugs.ll
    llvm/test/Transforms/Attributor/depgraph.ll
    llvm/test/Transforms/Attributor/internal-noalias.ll
    llvm/test/Transforms/Attributor/liveness_chains.ll
    llvm/test/Transforms/Attributor/lowerheap.ll
    llvm/test/Transforms/Attributor/misc.ll
    llvm/test/Transforms/Attributor/noalias.ll
    llvm/test/Transforms/Attributor/noundef.ll
    llvm/test/Transforms/Attributor/value-simplify-assume.ll
    llvm/test/Transforms/Attributor/value-simplify-pointer-info.ll
    llvm/unittests/Transforms/IPO/AttributorTest.cpp

Removed: 
    llvm/test/Transforms/Attributor/value-simplify-reachability.ll


################################################################################
diff  --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h
index 3d622fd434b9a..768e5ea2ec0e6 100644
--- a/llvm/include/llvm/Transforms/IPO/Attributor.h
+++ b/llvm/include/llvm/Transforms/IPO/Attributor.h
@@ -151,7 +151,6 @@ class Function;
 
 /// Abstract Attribute helper functions.
 namespace AA {
-using InstExclusionSetTy = SmallPtrSet<Instruction *, 4>;
 
 /// Flags to distinguish intra-procedural queries from *potentially*
 /// inter-procedural queries. Not that information can be valid for both and
@@ -353,26 +352,23 @@ bool isAssumedReadOnly(Attributor &A, const IRPosition &IRP,
 bool isAssumedReadNone(Attributor &A, const IRPosition &IRP,
                        const AbstractAttribute &QueryingAA, bool &IsKnown);
 
-/// Return true if \p ToI is potentially reachable from \p FromI without running
-/// into any instruction in \p ExclusionSet The two instructions do not need to
-/// be in the same function. \p GoBackwardsCB can be provided to convey domain
-/// knowledge about the "lifespan" the user is interested in. By default, the
-/// callers of \p FromI are checked as well to determine if \p ToI can be
-/// reached. If the query is not interested in callers beyond a certain point,
-/// e.g., a GPU kernel entry or the function containing an alloca, the
-/// \p GoBackwardsCB should return false.
+/// Return true if \p ToI is potentially reachable from \p FromI. The two
+/// instructions do not need to be in the same function. \p GoBackwardsCB
+/// can be provided to convey domain knowledge about the "lifespan" the user is
+/// interested in. By default, the callers of \p FromI are checked as well to
+/// determine if \p ToI can be reached. If the query is not interested in
+/// callers beyond a certain point, e.g., a GPU kernel entry or the function
+/// containing an alloca, the \p GoBackwardsCB should return false.
 bool isPotentiallyReachable(
     Attributor &A, const Instruction &FromI, const Instruction &ToI,
     const AbstractAttribute &QueryingAA,
-    const AA::InstExclusionSetTy *ExclusionSet = nullptr,
     std::function<bool(const Function &F)> GoBackwardsCB = nullptr);
 
 /// Same as above but it is sufficient to reach any instruction in \p ToFn.
 bool isPotentiallyReachable(
     Attributor &A, const Instruction &FromI, const Function &ToFn,
     const AbstractAttribute &QueryingAA,
-    const AA::InstExclusionSetTy *ExclusionSet = nullptr,
-    std::function<bool(const Function &F)> GoBackwardsCB = nullptr);
+    std::function<bool(const Function &F)> GoBackwardsCB);
 
 } // namespace AA
 
@@ -414,39 +410,6 @@ struct DenseMapInfo<AA::ValueScope> : public DenseMapInfo<unsigned char> {
   }
 };
 
-template <>
-struct DenseMapInfo<const AA::InstExclusionSetTy *>
-    : public DenseMapInfo<void *> {
-  using super = DenseMapInfo<void *>;
-  static inline const AA::InstExclusionSetTy *getEmptyKey() {
-    return static_cast<const AA::InstExclusionSetTy *>(super::getEmptyKey());
-  }
-  static inline const AA::InstExclusionSetTy *getTombstoneKey() {
-    return static_cast<const AA::InstExclusionSetTy *>(
-        super::getTombstoneKey());
-  }
-  static unsigned getHashValue(const AA::InstExclusionSetTy *BES) {
-    unsigned H = 0;
-    if (BES)
-      for (const auto *II : *BES)
-        H += DenseMapInfo<const Instruction *>::getHashValue(II);
-    return H;
-  }
-  static bool isEqual(const AA::InstExclusionSetTy *LHS,
-                      const AA::InstExclusionSetTy *RHS) {
-    if (LHS == RHS)
-      return true;
-    if (LHS == getEmptyKey() || RHS == getEmptyKey() ||
-        LHS == getTombstoneKey() || RHS == getTombstoneKey())
-      return false;
-    if (!LHS || !RHS)
-      return ((LHS && LHS->empty()) || (RHS && RHS->empty()));
-    if (LHS->size() != RHS->size())
-      return false;
-    return llvm::set_is_subset(*LHS, *RHS);
-  }
-};
-
 /// The value passed to the line option that defines the maximal initialization
 /// chain length.
 extern unsigned MaxInitializationChainLength;
@@ -1259,16 +1222,21 @@ struct InformationCache {
   /// Return the map conaining all the knowledge we have from `llvm.assume`s.
   const RetainedKnowledgeMap &getKnowledgeMap() const { return KnowledgeMap; }
 
-  /// Given \p BES, return a uniqued version. \p BES is destroyed in the
-  /// process.
-  const AA::InstExclusionSetTy *
-  getOrCreateUniqueBlockExecutionSet(const AA::InstExclusionSetTy *BES) {
-    auto It = BESets.find(BES);
-    if (It != BESets.end())
-      return *It;
-    auto *UniqueBES = new (Allocator) AA::InstExclusionSetTy(*BES);
-    BESets.insert(UniqueBES);
-    return UniqueBES;
+  /// Return if \p To is potentially reachable form \p From or not
+  /// If the same query was answered, return cached result
+  bool getPotentiallyReachable(const Instruction &From, const Instruction &To) {
+    auto KeyPair = std::make_pair(&From, &To);
+    auto Iter = PotentiallyReachableMap.find(KeyPair);
+    if (Iter != PotentiallyReachableMap.end())
+      return Iter->second;
+    const Function &F = *From.getFunction();
+    bool Result = true;
+    if (From.getFunction() == To.getFunction())
+      Result = isPotentiallyReachable(&From, &To, nullptr,
+                                      AG.getAnalysis<DominatorTreeAnalysis>(F),
+                                      AG.getAnalysis<LoopAnalysis>(F));
+    PotentiallyReachableMap.insert(std::make_pair(KeyPair, Result));
+    return Result;
   }
 
   /// Check whether \p F is part of module slice.
@@ -1337,15 +1305,16 @@ struct InformationCache {
   /// A container for all instructions that are only used by `llvm.assume`.
   SetVector<const Instruction *> AssumeOnlyValues;
 
-  /// Cache for block sets to allow reuse.
-  DenseSet<const AA::InstExclusionSetTy *> BESets;
-
   /// Getters for analysis.
   AnalysisGetter &AG;
 
   /// Set of inlineable functions
   SmallPtrSet<const Function *, 8> InlineableFunctions;
 
+  /// A map for caching results of queries for isPotentiallyReachable
+  DenseMap<std::pair<const Instruction *, const Instruction *>, bool>
+      PotentiallyReachableMap;
+
   /// The triple describing the target machine.
   Triple TargetTriple;
 
@@ -3423,30 +3392,42 @@ struct AAUndefinedBehavior
 };
 
 /// An abstract interface to determine reachability of point A to B.
-struct AAIntraFnReachability
-    : public StateWrapper<BooleanState, AbstractAttribute> {
+struct AAReachability : public StateWrapper<BooleanState, AbstractAttribute> {
   using Base = StateWrapper<BooleanState, AbstractAttribute>;
-  AAIntraFnReachability(const IRPosition &IRP, Attributor &A) : Base(IRP) {}
+  AAReachability(const IRPosition &IRP, Attributor &A) : Base(IRP) {}
 
   /// Returns true if 'From' instruction is assumed to reach, 'To' instruction.
   /// Users should provide two positions they are interested in, and the class
   /// determines (and caches) reachability.
-  virtual bool isAssumedReachable(
-      Attributor &A, const Instruction &From, const Instruction &To,
-      const AA::InstExclusionSetTy *ExclusionSet = nullptr) const = 0;
+  bool isAssumedReachable(Attributor &A, const Instruction &From,
+                          const Instruction &To) const {
+    if (!getState().isValidState())
+      return true;
+    return A.getInfoCache().getPotentiallyReachable(From, To);
+  }
+
+  /// Returns true if 'From' instruction is known to reach, 'To' instruction.
+  /// Users should provide two positions they are interested in, and the class
+  /// determines (and caches) reachability.
+  bool isKnownReachable(Attributor &A, const Instruction &From,
+                        const Instruction &To) const {
+    if (!getState().isValidState())
+      return false;
+    return A.getInfoCache().getPotentiallyReachable(From, To);
+  }
 
   /// Create an abstract attribute view for the position \p IRP.
-  static AAIntraFnReachability &createForPosition(const IRPosition &IRP,
-                                                  Attributor &A);
+  static AAReachability &createForPosition(const IRPosition &IRP,
+                                           Attributor &A);
 
   /// See AbstractAttribute::getName()
-  const std::string getName() const override { return "AAIntraFnReachability"; }
+  const std::string getName() const override { return "AAReachability"; }
 
   /// See AbstractAttribute::getIdAddr()
   const char *getIdAddr() const override { return &ID; }
 
   /// This function should return true if the type of the \p AA is
-  /// AAIntraFnReachability
+  /// AAReachability
   static bool classof(const AbstractAttribute *AA) {
     return (AA->getIdAddr() == &ID);
   }
@@ -4967,33 +4948,35 @@ struct AAExecutionDomain
 };
 
 /// An abstract Attribute for computing reachability between functions.
-struct AAInterFnReachability
+struct AAFunctionReachability
     : public StateWrapper<BooleanState, AbstractAttribute> {
   using Base = StateWrapper<BooleanState, AbstractAttribute>;
 
-  AAInterFnReachability(const IRPosition &IRP, Attributor &A) : Base(IRP) {}
+  AAFunctionReachability(const IRPosition &IRP, Attributor &A) : Base(IRP) {}
+
+  /// See AbstractAttribute::isQueryAA.
+  bool isQueryAA() const override { return true; }
 
   /// If the function represented by this possition can reach \p Fn.
-  bool canReach(Attributor &A, const Function &Fn) const {
-    Function *Scope = getAnchorScope();
-    if (!Scope || Scope->isDeclaration())
-      return true;
-    return instructionCanReach(A, Scope->getEntryBlock().front(), Fn);
-  }
+  virtual bool canReach(Attributor &A, const Function &Fn) const = 0;
+
+  /// Can \p CB reach \p Fn.
+  virtual bool canReach(Attributor &A, CallBase &CB,
+                        const Function &Fn) const = 0;
 
   /// Can  \p Inst reach \p Fn.
   /// See also AA::isPotentiallyReachable.
-  virtual bool instructionCanReach(
-      Attributor &A, const Instruction &Inst, const Function &Fn,
-      const AA::InstExclusionSetTy *ExclusionSet = nullptr,
-      SmallPtrSet<const Function *, 16> *Visited = nullptr) const = 0;
+  virtual bool instructionCanReach(Attributor &A, const Instruction &Inst,
+                                   const Function &Fn) const = 0;
 
   /// Create an abstract attribute view for the position \p IRP.
-  static AAInterFnReachability &createForPosition(const IRPosition &IRP,
-                                                  Attributor &A);
+  static AAFunctionReachability &createForPosition(const IRPosition &IRP,
+                                                   Attributor &A);
 
   /// See AbstractAttribute::getName()
-  const std::string getName() const override { return "AAInterFnReachability"; }
+  const std::string getName() const override {
+    return "AAFunctionReachability";
+  }
 
   /// See AbstractAttribute::getIdAddr()
   const char *getIdAddr() const override { return &ID; }
@@ -5005,6 +4988,10 @@ struct AAInterFnReachability
 
   /// Unique ID (due to the unique address)
   static const char ID;
+
+private:
+  /// Can this function reach a call with unknown calee.
+  virtual bool canReachUnknownCallee() const = 0;
 };
 
 /// An abstract interface for struct information.

diff  --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp
index bfc53aaa3ef43..a1bb3544ee8d9 100644
--- a/llvm/lib/Transforms/IPO/Attributor.cpp
+++ b/llvm/lib/Transforms/IPO/Attributor.cpp
@@ -570,27 +570,19 @@ static bool
 isPotentiallyReachable(Attributor &A, const Instruction &FromI,
                        const Instruction *ToI, const Function &ToFn,
                        const AbstractAttribute &QueryingAA,
-                       const AA::InstExclusionSetTy *ExclusionSet,
                        std::function<bool(const Function &F)> GoBackwardsCB) {
-  LLVM_DEBUG({
-    dbgs() << "[AA] isPotentiallyReachable @" << ToFn.getName() << " from "
-           << FromI << " [GBCB: " << bool(GoBackwardsCB) << "][#ExS: "
-           << (ExclusionSet ? std::to_string(ExclusionSet->size()) : "none")
-           << "]\n";
-    if (ExclusionSet)
-      for (auto *ES : *ExclusionSet)
-        dbgs() << *ES << "\n";
-  });
-
-  // If we can go arbitrarily backwards we will eventually reach an entry point
-  // that can reach ToI. Only if a set of blocks through which we cannot go is
-  // provided, or once we track internal functions not accessible from the
-  // outside, it makes sense to perform backwards analysis in the absence of a
-  // GoBackwardsCB.
-  if (!GoBackwardsCB && !ExclusionSet) {
+  LLVM_DEBUG(dbgs() << "[AA] isPotentiallyReachable @" << ToFn.getName()
+                    << " from " << FromI << " [GBCB: " << bool(GoBackwardsCB)
+                    << "]\n");
+
+  // TODO: If we can go arbitrarily backwards we will eventually reach an
+  // entry point that can reach ToI. Only once this takes a set of blocks
+  // through which we cannot go, or once we track internal functions not
+  // accessible from the outside, it makes sense to perform backwards analysis
+  // in the absence of a GoBackwardsCB.
+  if (!GoBackwardsCB) {
     LLVM_DEBUG(dbgs() << "[AA] check @" << ToFn.getName() << " from " << FromI
-                      << " is not checked backwards and does not have an "
-                         "exclusion set, abort\n");
+                      << " is not checked backwards, abort\n");
     return true;
   }
 
@@ -609,10 +601,9 @@ isPotentiallyReachable(Attributor &A, const Instruction &FromI,
         return true;
       LLVM_DEBUG(dbgs() << "[AA] check " << *ToI << " from " << *CurFromI
                         << " intraprocedurally\n");
-      const auto &ReachabilityAA = A.getAAFor<AAIntraFnReachability>(
+      const auto &ReachabilityAA = A.getAAFor<AAReachability>(
           QueryingAA, IRPosition::function(ToFn), DepClassTy::OPTIONAL);
-      bool Result =
-          ReachabilityAA.isAssumedReachable(A, *CurFromI, *ToI, ExclusionSet);
+      bool Result = ReachabilityAA.isAssumedReachable(A, *CurFromI, *ToI);
       LLVM_DEBUG(dbgs() << "[AA] " << *CurFromI << " "
                         << (Result ? "can potentially " : "cannot ") << "reach "
                         << *ToI << " [Intra]\n");
@@ -620,57 +611,15 @@ isPotentiallyReachable(Attributor &A, const Instruction &FromI,
         return true;
     }
 
-    bool Result = true;
-    if (!ToFn.isDeclaration() && ToI) {
-      const auto &ToReachabilityAA = A.getAAFor<AAIntraFnReachability>(
-          QueryingAA, IRPosition::function(ToFn), DepClassTy::OPTIONAL);
-      const Instruction &EntryI = ToFn.getEntryBlock().front();
-      Result =
-          ToReachabilityAA.isAssumedReachable(A, EntryI, *ToI, ExclusionSet);
-      LLVM_DEBUG(dbgs() << "[AA] Entry " << EntryI << " of @" << ToFn.getName()
-                        << " " << (Result ? "can potentially " : "cannot ")
-                        << "reach @" << *ToI << " [ToFn]\n");
-    }
-
-    if (Result) {
-      // The entry of the ToFn can reach the instruction ToI. If the current
-      // instruction is already known to reach the ToFn.
-      const auto &FnReachabilityAA = A.getAAFor<AAInterFnReachability>(
-          QueryingAA, IRPosition::function(*FromFn), DepClassTy::OPTIONAL);
-      Result = FnReachabilityAA.instructionCanReach(A, *CurFromI, ToFn,
-                                                    ExclusionSet);
-      LLVM_DEBUG(dbgs() << "[AA] " << *CurFromI << " in @" << FromFn->getName()
-                        << " " << (Result ? "can potentially " : "cannot ")
-                        << "reach @" << ToFn.getName() << " [FromFn]\n");
-      if (Result)
-        return true;
-    }
-
-    // TODO: Check assumed nounwind.
-    const auto &ReachabilityAA = A.getAAFor<AAIntraFnReachability>(
+    // Check if the current instruction is already known to reach the ToFn.
+    const auto &FnReachabilityAA = A.getAAFor<AAFunctionReachability>(
         QueryingAA, IRPosition::function(*FromFn), DepClassTy::OPTIONAL);
-    auto ReturnInstCB = [&](Instruction &Ret) {
-      bool Result =
-          ReachabilityAA.isAssumedReachable(A, *CurFromI, Ret, ExclusionSet);
-      LLVM_DEBUG(dbgs() << "[AA][Ret] " << *CurFromI << " "
-                        << (Result ? "can potentially " : "cannot ") << "reach "
-                        << Ret << " [Intra]\n");
-      return !Result;
-    };
-
-    // Check if we can reach returns.
-    bool UsedAssumedInformation = false;
-    if (A.checkForAllInstructions(ReturnInstCB, FromFn, QueryingAA,
-                                  {Instruction::Ret}, UsedAssumedInformation)) {
-      LLVM_DEBUG(dbgs() << "[AA] No return is reachable, done\n");
-      return false;
-    }
-
-    if (!GoBackwardsCB) {
-      LLVM_DEBUG(dbgs() << "[AA] check @" << ToFn.getName() << " from " << FromI
-                        << " is not checked backwards, abort\n");
+    bool Result = FnReachabilityAA.instructionCanReach(A, *CurFromI, ToFn);
+    LLVM_DEBUG(dbgs() << "[AA] " << *CurFromI << " in @" << FromFn->getName()
+                      << " " << (Result ? "can potentially " : "cannot ")
+                      << "reach @" << ToFn.getName() << " [FromFn]\n");
+    if (Result)
       return true;
-    }
 
     // If we do not go backwards from the FromFn we are done here and so far we
     // could not find a way to reach ToFn/ToI.
@@ -693,6 +642,7 @@ isPotentiallyReachable(Attributor &A, const Instruction &FromI,
       return true;
     };
 
+    bool UsedAssumedInformation = false;
     Result = !A.checkForAllCallSites(CheckCallSite, *FromFn,
                                      /* RequireAllCallSites */ true,
                                      &QueryingAA, UsedAssumedInformation);
@@ -713,20 +663,20 @@ isPotentiallyReachable(Attributor &A, const Instruction &FromI,
 bool AA::isPotentiallyReachable(
     Attributor &A, const Instruction &FromI, const Instruction &ToI,
     const AbstractAttribute &QueryingAA,
-    const AA::InstExclusionSetTy *ExclusionSet,
     std::function<bool(const Function &F)> GoBackwardsCB) {
+  LLVM_DEBUG(dbgs() << "[AA] isPotentiallyReachable " << ToI << " from "
+                    << FromI << " [GBCB: " << bool(GoBackwardsCB) << "]\n");
   const Function *ToFn = ToI.getFunction();
   return ::isPotentiallyReachable(A, FromI, &ToI, *ToFn, QueryingAA,
-                                  ExclusionSet, GoBackwardsCB);
+                                  GoBackwardsCB);
 }
 
 bool AA::isPotentiallyReachable(
     Attributor &A, const Instruction &FromI, const Function &ToFn,
     const AbstractAttribute &QueryingAA,
-    const AA::InstExclusionSetTy *ExclusionSet,
     std::function<bool(const Function &F)> GoBackwardsCB) {
   return ::isPotentiallyReachable(A, FromI, /* ToI */ nullptr, ToFn, QueryingAA,
-                                  ExclusionSet, GoBackwardsCB);
+                                  GoBackwardsCB);
 }
 
 /// Return true if \p New is equal or worse than \p Old.

diff  --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
index 6c2d56ffba2e9..e8c1b4a5ea4a5 100644
--- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
+++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
@@ -171,7 +171,7 @@ PIPE_OPERATOR(AANoCapture)
 PIPE_OPERATOR(AAValueSimplify)
 PIPE_OPERATOR(AANoFree)
 PIPE_OPERATOR(AAHeapToStack)
-PIPE_OPERATOR(AAIntraFnReachability)
+PIPE_OPERATOR(AAReachability)
 PIPE_OPERATOR(AAMemoryBehavior)
 PIPE_OPERATOR(AAMemoryLocation)
 PIPE_OPERATOR(AAValueConstantRange)
@@ -181,7 +181,7 @@ PIPE_OPERATOR(AAPotentialConstantValues)
 PIPE_OPERATOR(AAPotentialValues)
 PIPE_OPERATOR(AANoUndef)
 PIPE_OPERATOR(AACallEdges)
-PIPE_OPERATOR(AAInterFnReachability)
+PIPE_OPERATOR(AAFunctionReachability)
 PIPE_OPERATOR(AAPointerInfo)
 PIPE_OPERATOR(AAAssumptionInfo)
 
@@ -1160,16 +1160,7 @@ struct AAPointerInfoImpl
         };
     }
 
-    // Set of accesses/instructions that will overwrite the result and are
-    // therefore blockers in the reachability traversal.
-    AA::InstExclusionSetTy ExclusionSet;
-
     auto AccessCB = [&](const Access &Acc, bool Exact) {
-      if (Exact && Acc.isMustAccess() && Acc.getRemoteInst() != &I) {
-        if (Acc.isWrite() || (isa<LoadInst>(I) && Acc.isWriteOrAssumption()))
-          ExclusionSet.insert(Acc.getRemoteInst());
-      }
-
       if ((!FindInterferingWrites || !Acc.isWriteOrAssumption()) &&
           (!FindInterferingReads || !Acc.isRead()))
         return true;
@@ -1197,11 +1188,11 @@ struct AAPointerInfoImpl
     // hide the effect of this one.
     auto CanSkipAccess = [&](const Access &Acc, bool Exact) {
       if ((!Acc.isWriteOrAssumption() ||
-           !AA::isPotentiallyReachable(A, *Acc.getRemoteInst(), I, QueryingAA,
-                                       &ExclusionSet, IsLiveInCalleeCB)) &&
+           !AA::isPotentiallyReachable(A, *Acc.getLocalInst(), I, QueryingAA,
+                                       IsLiveInCalleeCB)) &&
           (!Acc.isRead() ||
-           !AA::isPotentiallyReachable(A, I, *Acc.getRemoteInst(), QueryingAA,
-                                       &ExclusionSet, IsLiveInCalleeCB)))
+           !AA::isPotentiallyReachable(A, I, *Acc.getLocalInst(), QueryingAA,
+                                       IsLiveInCalleeCB)))
         return true;
 
       if (!DT || !UseDominanceReasoning)
@@ -2758,9 +2749,9 @@ struct AANoRecurseFunction final : AANoRecurseImpl {
       return ChangeStatus::UNCHANGED;
     }
 
-    const AAInterFnReachability &EdgeReachability =
-        A.getAAFor<AAInterFnReachability>(*this, getIRPosition(),
-                                          DepClassTy::REQUIRED);
+    const AAFunctionReachability &EdgeReachability =
+        A.getAAFor<AAFunctionReachability>(*this, getIRPosition(),
+                                           DepClassTy::REQUIRED);
     if (EdgeReachability.canReach(A, *getAnchorScope()))
       return indicatePessimisticFixpoint();
     return ChangeStatus::UNCHANGED;
@@ -3273,245 +3264,30 @@ struct AAWillReturnCallSite final : AAWillReturnImpl {
 };
 } // namespace
 
-/// -------------------AAIntraFnReachability Attribute--------------------------
-
-/// All information associated with a reachability query. This boilerplate code
-/// is used by both AAIntraFnReachability and AAInterFnReachability, with
-/// 
diff erent \p ToTy values.
-template <typename ToTy> struct ReachabilityQueryInfo {
-  enum class Reachable {
-    No,
-    Yes,
-  };
-
-  /// Start here,
-  const Instruction *From = nullptr;
-  /// reach this place,
-  const ToTy *To = nullptr;
-  /// without going through any of these instructions,
-  const AA::InstExclusionSetTy *ExclusionSet = nullptr;
-  /// and remember if it worked:
-  Reachable Result = Reachable::No;
-
-  ReachabilityQueryInfo(const Instruction *From, const ToTy *To)
-      : From(From), To(To) {}
-
-  /// Constructor replacement to ensure unique and stable sets are used for the
-  /// cache.
-  ReachabilityQueryInfo(Attributor &A, const Instruction &From, const ToTy &To,
-                        const AA::InstExclusionSetTy *ES)
-      : From(&From), To(&To), ExclusionSet(ES) {
-
-    if (ExclusionSet && !ExclusionSet->empty()) {
-      ExclusionSet =
-          A.getInfoCache().getOrCreateUniqueBlockExecutionSet(ExclusionSet);
-    } else {
-      ExclusionSet = nullptr;
-    }
-  }
-
-  ReachabilityQueryInfo(const ReachabilityQueryInfo &RQI)
-      : From(RQI.From), To(RQI.To), ExclusionSet(RQI.ExclusionSet) {
-    assert(RQI.Result == Reachable::No &&
-           "Didn't expect to copy an explored RQI!");
-  }
-};
-
-namespace llvm {
-template <typename ToTy> struct DenseMapInfo<ReachabilityQueryInfo<ToTy> *> {
-  using InstSetDMI = DenseMapInfo<const AA::InstExclusionSetTy *>;
-  using PairDMI = DenseMapInfo<std::pair<const Instruction *, const ToTy *>>;
-
-  static ReachabilityQueryInfo<ToTy> EmptyKey;
-  static ReachabilityQueryInfo<ToTy> TombstoneKey;
-
-  static inline ReachabilityQueryInfo<ToTy> *getEmptyKey() { return &EmptyKey; }
-  static inline ReachabilityQueryInfo<ToTy> *getTombstoneKey() {
-    return &TombstoneKey;
-  }
-  static unsigned getHashValue(const ReachabilityQueryInfo<ToTy> *RQI) {
-    unsigned H = PairDMI ::getHashValue({RQI->From, RQI->To});
-    H += InstSetDMI::getHashValue(RQI->ExclusionSet);
-    return H;
-  }
-  static bool isEqual(const ReachabilityQueryInfo<ToTy> *LHS,
-                      const ReachabilityQueryInfo<ToTy> *RHS) {
-    if (!PairDMI::isEqual({LHS->From, LHS->To}, {RHS->From, RHS->To}))
-      return false;
-    return InstSetDMI::isEqual(LHS->ExclusionSet, RHS->ExclusionSet);
-  }
-};
-
-#define DefineKeys(ToTy)                                                       \
-  template <>                                                                  \
-  ReachabilityQueryInfo<ToTy>                                                  \
-      DenseMapInfo<ReachabilityQueryInfo<ToTy> *>::EmptyKey =                  \
-          ReachabilityQueryInfo<ToTy>(                                         \
-              DenseMapInfo<const Instruction *>::getEmptyKey(),                \
-              DenseMapInfo<const ToTy *>::getEmptyKey());                      \
-  template <>                                                                  \
-  ReachabilityQueryInfo<ToTy>                                                  \
-      DenseMapInfo<ReachabilityQueryInfo<ToTy> *>::TombstoneKey =              \
-          ReachabilityQueryInfo<ToTy>(                                         \
-              DenseMapInfo<const Instruction *>::getTombstoneKey(),            \
-              DenseMapInfo<const ToTy *>::getTombstoneKey());
-
-DefineKeys(Instruction) DefineKeys(Function)
-#undef DefineKeys
-
-} // namespace llvm
+/// -------------------AAReachability Attribute--------------------------
 
 namespace {
-
-template <typename BaseTy, typename ToTy>
-struct CachedReachabilityAA : public BaseTy {
-  using RQITy = ReachabilityQueryInfo<ToTy>;
-
-  CachedReachabilityAA<BaseTy, ToTy>(const IRPosition &IRP, Attributor &A)
-      : BaseTy(IRP, A) {}
-
-  /// See AbstractAttribute::isQueryAA.
-  bool isQueryAA() const override { return true; }
-
-  /// See AbstractAttribute::updateImpl(...).
-  ChangeStatus updateImpl(Attributor &A) override {
-    ChangeStatus Changed = ChangeStatus::UNCHANGED;
-    InUpdate = true;
-    for (RQITy *RQI : QueryVector) {
-      if (RQI->Result == RQITy::Reachable::No && isReachableImpl(A, *RQI))
-        Changed = ChangeStatus::CHANGED;
-    }
-    InUpdate = false;
-    return Changed;
-  }
-
-  virtual bool isReachableImpl(Attributor &A, RQITy &RQI) = 0;
-
-  bool rememberResult(Attributor &A, typename RQITy::Reachable Result,
-                      RQITy &RQI) {
-    if (Result == RQITy::Reachable::No) {
-      if (!InUpdate)
-        A.registerForUpdate(*this);
-      return false;
-    }
-    assert(RQI.Result == RQITy::Reachable::No && "Already reachable?");
-    RQI.Result = Result;
-    return true;
-  }
+struct AAReachabilityImpl : AAReachability {
+  AAReachabilityImpl(const IRPosition &IRP, Attributor &A)
+      : AAReachability(IRP, A) {}
 
   const std::string getAsStr() const override {
     // TODO: Return the number of reachable queries.
-    return "#queries(" + std::to_string(QueryVector.size()) + ")";
+    return "reachable";
   }
 
-  RQITy *checkQueryCache(Attributor &A, RQITy &StackRQI,
-                         typename RQITy::Reachable &Result) {
-    if (!this->getState().isValidState()) {
-      Result = RQITy::Reachable::Yes;
-      return nullptr;
-    }
-
-    auto It = QueryCache.find(&StackRQI);
-    if (It != QueryCache.end()) {
-      Result = (*It)->Result;
-      return nullptr;
-    }
-
-    RQITy *RQIPtr = new (A.Allocator) RQITy(StackRQI);
-    QueryVector.push_back(RQIPtr);
-    QueryCache.insert(RQIPtr);
-    return RQIPtr;
+  /// See AbstractAttribute::updateImpl(...).
+  ChangeStatus updateImpl(Attributor &A) override {
+    return ChangeStatus::UNCHANGED;
   }
-
-private:
-  bool InUpdate = false;
-  SmallVector<RQITy *> QueryVector;
-  DenseSet<RQITy *> QueryCache;
 };
 
-struct AAIntraFnReachabilityFunction final
-    : public CachedReachabilityAA<AAIntraFnReachability, Instruction> {
-  AAIntraFnReachabilityFunction(const IRPosition &IRP, Attributor &A)
-      : CachedReachabilityAA<AAIntraFnReachability, Instruction>(IRP, A) {}
-
-  bool isAssumedReachable(
-      Attributor &A, const Instruction &From, const Instruction &To,
-      const AA::InstExclusionSetTy *ExclusionSet) const override {
-    auto *NonConstThis = const_cast<AAIntraFnReachabilityFunction *>(this);
-    if (&From == &To)
-      return true;
-
-    RQITy StackRQI(A, From, To, ExclusionSet);
-    typename RQITy::Reachable Result;
-    if (RQITy *RQIPtr = NonConstThis->checkQueryCache(A, StackRQI, Result)) {
-      return NonConstThis->isReachableImpl(A, *RQIPtr);
-    }
-    return Result == RQITy::Reachable::Yes;
-  }
-
-  bool isReachableImpl(Attributor &A, RQITy &RQI) override {
-    const Instruction *Origin = RQI.From;
-
-    auto WillReachInBlock = [=](const Instruction &From, const Instruction &To,
-                                const AA::InstExclusionSetTy *ExclusionSet) {
-      const Instruction *IP = &From;
-      while (IP && IP != &To) {
-        if (ExclusionSet && IP != Origin && ExclusionSet->count(IP))
-          break;
-        IP = IP->getNextNode();
-      }
-      return IP == &To;
-    };
-
-    const BasicBlock *FromBB = RQI.From->getParent();
-    const BasicBlock *ToBB = RQI.To->getParent();
-    assert(FromBB->getParent() == ToBB->getParent() &&
-           "Not an intra-procedural query!");
-
-    // Check intra-block reachability, however, other reaching paths are still
-    // possible.
-    if (FromBB == ToBB &&
-        WillReachInBlock(*RQI.From, *RQI.To, RQI.ExclusionSet))
-      return rememberResult(A, RQITy::Reachable::Yes, RQI);
-
-    SmallPtrSet<const BasicBlock *, 16> ExclusionBlocks;
-    if (RQI.ExclusionSet)
-      for (auto *I : *RQI.ExclusionSet)
-        ExclusionBlocks.insert(I->getParent());
-
-    // Check if we make it out of the FromBB block at all.
-    if (ExclusionBlocks.count(FromBB) &&
-        !WillReachInBlock(*RQI.From, *FromBB->getTerminator(),
-                          RQI.ExclusionSet))
-      return rememberResult(A, RQITy::Reachable::No, RQI);
-
-    SmallPtrSet<const BasicBlock *, 16> Visited;
-    SmallVector<const BasicBlock *, 16> Worklist;
-    Worklist.push_back(FromBB);
-
-    auto &LivenessAA =
-        A.getAAFor<AAIsDead>(*this, getIRPosition(), DepClassTy::OPTIONAL);
-    while (!Worklist.empty()) {
-      const BasicBlock *BB = Worklist.pop_back_val();
-      if (!Visited.insert(BB).second)
-        continue;
-      for (const BasicBlock *SuccBB : successors(BB)) {
-        if (LivenessAA.isEdgeDead(BB, SuccBB))
-          continue;
-        if (SuccBB == ToBB &&
-            WillReachInBlock(SuccBB->front(), *RQI.To, RQI.ExclusionSet))
-          return rememberResult(A, RQITy::Reachable::Yes, RQI);
-        if (ExclusionBlocks.count(SuccBB))
-          continue;
-        Worklist.push_back(SuccBB);
-      }
-    }
-
-    return rememberResult(A, RQITy::Reachable::No, RQI);
-  }
+struct AAReachabilityFunction final : public AAReachabilityImpl {
+  AAReachabilityFunction(const IRPosition &IRP, Attributor &A)
+      : AAReachabilityImpl(IRP, A) {}
 
   /// See AbstractAttribute::trackStatistics()
-  void trackStatistics() const override {}
+  void trackStatistics() const override { STATS_DECLTRACK_FN_ATTR(reachable); }
 };
 } // namespace
 
@@ -3754,7 +3530,7 @@ struct AANoAliasCallSiteArgument final : AANoAliasImpl {
         }
 
         if (!AA::isPotentiallyReachable(
-                A, *UserI, *getCtxI(), *this, /* ExclusionSet */ nullptr,
+                A, *UserI, *getCtxI(), *this,
                 [ScopeFn](const Function &Fn) { return &Fn != ScopeFn; }))
           return true;
       }
@@ -5383,7 +5159,7 @@ struct AAInstanceInfoImpl : public AAInstanceInfo {
         // If this call base might reach the scope again we might forward the
         // argument back here. This is very conservative.
         if (AA::isPotentiallyReachable(
-                A, *CB, *Scope, *this, /* ExclusionSet */ nullptr,
+                A, *CB, *Scope, *this,
                 [Scope](const Function &Fn) { return &Fn != Scope; }))
           return false;
         return true;
@@ -10098,98 +9874,289 @@ struct AACallEdgesFunction : public AACallEdgesImpl {
   }
 };
 
-/// -------------------AAInterFnReachability Attribute--------------------------
+struct AAFunctionReachabilityFunction : public AAFunctionReachability {
+private:
+  struct QuerySet {
+    void markReachable(const Function &Fn) {
+      Reachable.insert(&Fn);
+      Unreachable.erase(&Fn);
+    }
 
-struct AAInterFnReachabilityFunction
-    : public CachedReachabilityAA<AAInterFnReachability, Function> {
-  AAInterFnReachabilityFunction(const IRPosition &IRP, Attributor &A)
-      : CachedReachabilityAA<AAInterFnReachability, Function>(IRP, A) {}
+    /// If there is no information about the function std::nullopt is returned.
+    std::optional<bool> isCachedReachable(const Function &Fn) {
+      // Assume that we can reach the function.
+      // TODO: Be more specific with the unknown callee.
+      if (CanReachUnknownCallee)
+        return true;
 
-  bool instructionCanReach(
-      Attributor &A, const Instruction &From, const Function &To,
-      const AA::InstExclusionSetTy *ExclusionSet,
-      SmallPtrSet<const Function *, 16> *Visited) const override {
-    assert(From.getFunction() == getAnchorScope() && "Queried the wrong AA!");
-    auto *NonConstThis = const_cast<AAInterFnReachabilityFunction *>(this);
+      if (Reachable.count(&Fn))
+        return true;
 
-    RQITy StackRQI(A, From, To, ExclusionSet);
-    typename RQITy::Reachable Result;
-    if (RQITy *RQIPtr = NonConstThis->checkQueryCache(A, StackRQI, Result))
-      return NonConstThis->isReachableImpl(A, *RQIPtr);
-    return Result == RQITy::Reachable::Yes;
-  }
+      if (Unreachable.count(&Fn))
+        return false;
 
-  bool isReachableImpl(Attributor &A, RQITy &RQI) override {
-    return isReachableImpl(A, RQI, nullptr);
-  }
+      return std::nullopt;
+    }
 
-  bool isReachableImpl(Attributor &A, RQITy &RQI,
-                       SmallPtrSet<const Function *, 16> *Visited) {
+    /// Set of functions that we know for sure is reachable.
+    DenseSet<const Function *> Reachable;
 
-    SmallPtrSet<const Function *, 16> LocalVisited;
-    if (!Visited)
-      Visited = &LocalVisited;
+    /// Set of functions that are unreachable, but might become reachable.
+    DenseSet<const Function *> Unreachable;
 
-    const auto &IntraFnReachability = A.getAAFor<AAIntraFnReachability>(
-        *this, IRPosition::function(*RQI.From->getFunction()),
-        DepClassTy::OPTIONAL);
+    /// If we can reach a function with a call to a unknown function we assume
+    /// that we can reach any function.
+    bool CanReachUnknownCallee = false;
+  };
+
+  struct QueryResolver : public QuerySet {
+    ChangeStatus update(Attributor &A, const AAFunctionReachability &AA,
+                        ArrayRef<const AACallEdges *> AAEdgesList) {
+      ChangeStatus Change = ChangeStatus::UNCHANGED;
+
+      for (const auto *AAEdges : AAEdgesList) {
+        if (AAEdges->hasUnknownCallee()) {
+          if (!CanReachUnknownCallee) {
+            LLVM_DEBUG(dbgs()
+                       << "[QueryResolver] Edges include unknown callee!\n");
+            Change = ChangeStatus::CHANGED;
+          }
+          CanReachUnknownCallee = true;
+          return Change;
+        }
+      }
 
+      for (const Function *Fn : make_early_inc_range(Unreachable)) {
+        if (checkIfReachable(A, AA, AAEdgesList, *Fn)) {
+          Change = ChangeStatus::CHANGED;
+          markReachable(*Fn);
+        }
+      }
+      return Change;
+    }
+
+    bool isReachable(Attributor &A, AAFunctionReachability &AA,
+                     ArrayRef<const AACallEdges *> AAEdgesList,
+                     const Function &Fn) {
+      std::optional<bool> Cached = isCachedReachable(Fn);
+      if (Cached)
+        return Cached.value();
+
+      // The query was not cached, thus it is new. We need to request an update
+      // explicitly to make sure this the information is properly run to a
+      // fixpoint.
+      A.registerForUpdate(AA);
+
+      // We need to assume that this function can't reach Fn to prevent
+      // an infinite loop if this function is recursive.
+      Unreachable.insert(&Fn);
+
+      bool Result = checkIfReachable(A, AA, AAEdgesList, Fn);
+      if (Result)
+        markReachable(Fn);
+      return Result;
+    }
+
+    bool checkIfReachable(Attributor &A, const AAFunctionReachability &AA,
+                          ArrayRef<const AACallEdges *> AAEdgesList,
+                          const Function &Fn) const {
+
+      // Handle the most trivial case first.
+      for (const auto *AAEdges : AAEdgesList) {
+        const SetVector<Function *> &Edges = AAEdges->getOptimisticEdges();
+
+        if (Edges.count(const_cast<Function *>(&Fn)))
+          return true;
+      }
+
+      SmallVector<const AAFunctionReachability *, 8> Deps;
+      for (const auto &AAEdges : AAEdgesList) {
+        const SetVector<Function *> &Edges = AAEdges->getOptimisticEdges();
+
+        for (Function *Edge : Edges) {
+          // Functions that do not call back into the module can be ignored.
+          if (Edge->hasFnAttribute(Attribute::NoCallback))
+            continue;
+
+          // We don't need a dependency if the result is reachable.
+          const AAFunctionReachability &EdgeReachability =
+              A.getAAFor<AAFunctionReachability>(
+                  AA, IRPosition::function(*Edge), DepClassTy::NONE);
+          Deps.push_back(&EdgeReachability);
+
+          if (EdgeReachability.canReach(A, Fn))
+            return true;
+        }
+      }
+
+      // The result is false for now, set dependencies and leave.
+      for (const auto *Dep : Deps)
+        A.recordDependence(*Dep, AA, DepClassTy::REQUIRED);
+
+      return false;
+    }
+  };
+
+  /// Get call edges that can be reached by this instruction.
+  bool getReachableCallEdges(Attributor &A, const AAReachability &Reachability,
+                             const Instruction &Inst,
+                             SmallVector<const AACallEdges *> &Result) const {
     // Determine call like instructions that we can reach from the inst.
-    SmallVector<CallBase *> ReachableCallBases;
     auto CheckCallBase = [&](Instruction &CBInst) {
-      if (IntraFnReachability.isAssumedReachable(A, *RQI.From, CBInst,
-                                                 RQI.ExclusionSet))
-        ReachableCallBases.push_back(cast<CallBase>(&CBInst));
+      if (!Reachability.isAssumedReachable(A, Inst, CBInst))
+        return true;
+
+      auto &CB = cast<CallBase>(CBInst);
+      const AACallEdges &AAEdges = A.getAAFor<AACallEdges>(
+          *this, IRPosition::callsite_function(CB), DepClassTy::REQUIRED);
+
+      Result.push_back(&AAEdges);
       return true;
     };
 
     bool UsedAssumedInformation = false;
-    if (!A.checkForAllCallLikeInstructions(CheckCallBase, *this,
-                                           UsedAssumedInformation,
-                                           /* CheckBBLivenessOnly */ true))
-      return rememberResult(A, RQITy::Reachable::Yes, RQI);
+    return A.checkForAllCallLikeInstructions(CheckCallBase, *this,
+                                             UsedAssumedInformation,
+                                             /* CheckBBLivenessOnly */ true);
+  }
 
-    for (CallBase *CB : ReachableCallBases) {
-      auto &CBEdges = A.getAAFor<AACallEdges>(
-          *this, IRPosition::callsite_function(*CB), DepClassTy::OPTIONAL);
-      if (!CBEdges.getState().isValidState())
-        return rememberResult(A, RQITy::Reachable::Yes, RQI);
-      // TODO Check To backwards in this case.
-      if (CBEdges.hasUnknownCallee())
-        return rememberResult(A, RQITy::Reachable::Yes, RQI);
+public:
+  AAFunctionReachabilityFunction(const IRPosition &IRP, Attributor &A)
+      : AAFunctionReachability(IRP, A) {}
 
-      for (Function *Fn : CBEdges.getOptimisticEdges()) {
-        if (Fn == RQI.To)
-          return rememberResult(A, RQITy::Reachable::Yes, RQI);
-        if (!Visited->insert(Fn).second)
-          continue;
-        if (Fn->isDeclaration()) {
-          if (Fn->hasFnAttribute(Attribute::NoCallback))
-            continue;
-          // TODO Check To backwards in this case.
-          return rememberResult(A, RQITy::Reachable::Yes, RQI);
-        }
+  bool canReach(Attributor &A, const Function &Fn) const override {
+    if (!isValidState())
+      return true;
+
+    const AACallEdges &AAEdges =
+        A.getAAFor<AACallEdges>(*this, getIRPosition(), DepClassTy::REQUIRED);
+
+    // Attributor returns attributes as const, so this function has to be
+    // const for users of this attribute to use it without having to do
+    // a const_cast.
+    // This is a hack for us to be able to cache queries.
+    auto *NonConstThis = const_cast<AAFunctionReachabilityFunction *>(this);
+    bool Result = NonConstThis->WholeFunction.isReachable(A, *NonConstThis,
+                                                          {&AAEdges}, Fn);
+
+    return Result;
+  }
+
+  /// Can \p CB reach \p Fn
+  bool canReach(Attributor &A, CallBase &CB,
+                const Function &Fn) const override {
+    if (!isValidState())
+      return true;
+
+    const AACallEdges &AAEdges = A.getAAFor<AACallEdges>(
+        *this, IRPosition::callsite_function(CB), DepClassTy::REQUIRED);
+
+    // Attributor returns attributes as const, so this function has to be
+    // const for users of this attribute to use it without having to do
+    // a const_cast.
+    // This is a hack for us to be able to cache queries.
+    auto *NonConstThis = const_cast<AAFunctionReachabilityFunction *>(this);
+    QueryResolver &CBQuery = NonConstThis->CBQueries[&CB];
+
+    bool Result = CBQuery.isReachable(A, *NonConstThis, {&AAEdges}, Fn);
 
-        const AAInterFnReachability *InterFnReachability = this;
-        if (Fn != getAnchorScope())
-          InterFnReachability = &A.getAAFor<AAInterFnReachability>(
-              *this, IRPosition::function(*Fn), DepClassTy::OPTIONAL);
+    return Result;
+  }
+
+  bool instructionCanReach(Attributor &A, const Instruction &Inst,
+                           const Function &Fn) const override {
+    if (!isValidState())
+      return true;
+
+    const auto &Reachability = A.getAAFor<AAReachability>(
+        *this, IRPosition::function(*getAssociatedFunction()),
+        DepClassTy::REQUIRED);
+
+    SmallVector<const AACallEdges *> CallEdges;
+    bool AllKnown = getReachableCallEdges(A, Reachability, Inst, CallEdges);
+    // Attributor returns attributes as const, so this function has to be
+    // const for users of this attribute to use it without having to do
+    // a const_cast.
+    // This is a hack for us to be able to cache queries.
+    auto *NonConstThis = const_cast<AAFunctionReachabilityFunction *>(this);
+    QueryResolver &InstQSet = NonConstThis->InstQueries[&Inst];
+    if (!AllKnown) {
+      LLVM_DEBUG(dbgs() << "[AAReachability] Not all reachable edges known, "
+                           "may reach unknown callee!\n");
+      InstQSet.CanReachUnknownCallee = true;
+    }
+
+    return InstQSet.isReachable(A, *NonConstThis, CallEdges, Fn);
+  }
 
-        const Instruction &FnFirstInst = Fn->getEntryBlock().front();
-        if (InterFnReachability->instructionCanReach(A, FnFirstInst, *RQI.To,
-                                                     RQI.ExclusionSet, Visited))
-          return rememberResult(A, RQITy::Reachable::Yes, RQI);
+  /// See AbstractAttribute::updateImpl(...).
+  ChangeStatus updateImpl(Attributor &A) override {
+    const AACallEdges &AAEdges =
+        A.getAAFor<AACallEdges>(*this, getIRPosition(), DepClassTy::REQUIRED);
+    ChangeStatus Change = ChangeStatus::UNCHANGED;
+
+    Change |= WholeFunction.update(A, *this, {&AAEdges});
+
+    for (auto &CBPair : CBQueries) {
+      const AACallEdges &AAEdges = A.getAAFor<AACallEdges>(
+          *this, IRPosition::callsite_function(*CBPair.first),
+          DepClassTy::REQUIRED);
+
+      Change |= CBPair.second.update(A, *this, {&AAEdges});
+    }
+
+    // Update the Instruction queries.
+    if (!InstQueries.empty()) {
+      const AAReachability *Reachability = &A.getAAFor<AAReachability>(
+          *this, IRPosition::function(*getAssociatedFunction()),
+          DepClassTy::REQUIRED);
+
+      // Check for local callbases first.
+      for (auto &InstPair : InstQueries) {
+        SmallVector<const AACallEdges *> CallEdges;
+        bool AllKnown =
+            getReachableCallEdges(A, *Reachability, *InstPair.first, CallEdges);
+        // Update will return change if we this effects any queries.
+        if (!AllKnown) {
+          LLVM_DEBUG(dbgs() << "[AAReachability] Not all reachable edges "
+                               "known, may reach unknown callee!\n");
+          InstPair.second.CanReachUnknownCallee = true;
+        }
+        Change |= InstPair.second.update(A, *this, CallEdges);
       }
     }
 
-    return rememberResult(A, RQITy::Reachable::No, RQI);
+    return Change;
+  }
+
+  const std::string getAsStr() const override {
+    size_t QueryCount =
+        WholeFunction.Reachable.size() + WholeFunction.Unreachable.size();
+
+    return "FunctionReachability [" +
+           (canReachUnknownCallee()
+                ? "unknown"
+                : (std::to_string(WholeFunction.Reachable.size()) + "," +
+                   std::to_string(QueryCount))) +
+           "]";
   }
 
   void trackStatistics() const override {}
 
 private:
-  SmallVector<RQITy *> QueryVector;
-  DenseSet<RQITy *> QueryCache;
+  bool canReachUnknownCallee() const override {
+    return WholeFunction.CanReachUnknownCallee;
+  }
+
+  /// Used to answer if a the whole function can reacha a specific function.
+  QueryResolver WholeFunction;
+
+  /// Used to answer if a call base inside this function can reach a specific
+  /// function.
+  MapVector<const CallBase *, QueryResolver> CBQueries;
+
+  /// This is for instruction queries than scan "forward".
+  MapVector<const Instruction *, QueryResolver> InstQueries;
 };
 } // namespace
 
@@ -11191,7 +11158,7 @@ const char AANoRecurse::ID = 0;
 const char AAWillReturn::ID = 0;
 const char AAUndefinedBehavior::ID = 0;
 const char AANoAlias::ID = 0;
-const char AAIntraFnReachability::ID = 0;
+const char AAReachability::ID = 0;
 const char AANoReturn::ID = 0;
 const char AAIsDead::ID = 0;
 const char AADereferenceable::ID = 0;
@@ -11208,7 +11175,7 @@ const char AAPotentialConstantValues::ID = 0;
 const char AAPotentialValues::ID = 0;
 const char AANoUndef::ID = 0;
 const char AACallEdges::ID = 0;
-const char AAInterFnReachability::ID = 0;
+const char AAFunctionReachability::ID = 0;
 const char AAPointerInfo::ID = 0;
 const char AAAssumptionInfo::ID = 0;
 
@@ -11333,9 +11300,9 @@ CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAIsDead)
 CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoFree)
 
 CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAHeapToStack)
+CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAReachability)
 CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAUndefinedBehavior)
-CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAIntraFnReachability)
-CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAInterFnReachability)
+CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAFunctionReachability)
 
 CREATE_NON_RET_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAMemoryBehavior)
 

diff  --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/alloca-as.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/alloca-as.ll
index afb7358473691..53719c2b1e7af 100644
--- a/llvm/test/Transforms/Attributor/ArgumentPromotion/alloca-as.ll
+++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/alloca-as.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
-; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
 ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
 
 target datalayout = "A7"

diff  --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll
index d691d79dac264..77e8a214b81b8 100644
--- a/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll
+++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
-; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
 ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
 
 declare void @sink(i32)

diff  --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/naked_functions.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/naked_functions.ll
index 257ad473afc7d..c10674f095381 100644
--- a/llvm/test/Transforms/Attributor/ArgumentPromotion/naked_functions.ll
+++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/naked_functions.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
-; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
 ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
 
 ; Don't promote paramaters of/arguments to naked functions

diff  --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/nonzero-address-spaces.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/nonzero-address-spaces.ll
index 4565b9cc33e26..3f56bc0b94821 100644
--- a/llvm/test/Transforms/Attributor/ArgumentPromotion/nonzero-address-spaces.ll
+++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/nonzero-address-spaces.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
-; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
 ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
 
 ; ArgumentPromotion should preserve the default function address space

diff  --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/pr27568.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/pr27568.ll
index 32b9f8714803b..15dcfc9f20f14 100644
--- a/llvm/test/Transforms/Attributor/ArgumentPromotion/pr27568.ll
+++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/pr27568.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
-; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
 ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
 target triple = "x86_64-pc-windows-msvc"
 

diff  --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/profile.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/profile.ll
index f1b79c87af934..13cc1794f9ab8 100644
--- a/llvm/test/Transforms/Attributor/ArgumentPromotion/profile.ll
+++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/profile.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
-; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
 ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
 target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128"
 

diff  --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/variadic.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/variadic.ll
index e017d85181233..e9a09a6a2f1dc 100644
--- a/llvm/test/Transforms/Attributor/ArgumentPromotion/variadic.ll
+++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/variadic.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
-; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
 ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
 
 ; Unused arguments from variadic functions cannot be eliminated as that changes

diff  --git a/llvm/test/Transforms/Attributor/IPConstantProp/naked-return.ll b/llvm/test/Transforms/Attributor/IPConstantProp/naked-return.ll
index 1c9acece2c2a0..3e23a32d313fd 100644
--- a/llvm/test/Transforms/Attributor/IPConstantProp/naked-return.ll
+++ b/llvm/test/Transforms/Attributor/IPConstantProp/naked-return.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
-; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
 ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
 
 target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32"

diff  --git a/llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll b/llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll
index 0347cc3aba8ae..3536ee0849bfb 100644
--- a/llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll
+++ b/llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
-; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=5 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=11 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
 ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
 
 ;; This function returns its second argument on all return statements

diff  --git a/llvm/test/Transforms/Attributor/cgscc_bugs.ll b/llvm/test/Transforms/Attributor/cgscc_bugs.ll
index 8cf8280a4dc8a..22332b91c0c50 100644
--- a/llvm/test/Transforms/Attributor/cgscc_bugs.ll
+++ b/llvm/test/Transforms/Attributor/cgscc_bugs.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
-; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=6 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
 ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
 
 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"

diff  --git a/llvm/test/Transforms/Attributor/depgraph.ll b/llvm/test/Transforms/Attributor/depgraph.ll
index 901e95f9de2f4..0531916d8c8a4 100644
--- a/llvm/test/Transforms/Attributor/depgraph.ll
+++ b/llvm/test/Transforms/Attributor/depgraph.ll
@@ -144,9 +144,9 @@ define i32* @checkAndAdvance(i32* align 16 %0) {
 ; GRAPH-EMPTY:
 ; GRAPH-NEXT: [AANoRecurse] for CtxI '  %2 = load i32, i32* %0, align 4' at position {fn:checkAndAdvance [checkAndAdvance at -1]} with state may-recurse
 ; GRAPH-EMPTY:
-; GRAPH-NEXT: [AAInterFnReachability] for CtxI ' %2 = load i32, i32* %0, align 4' at position {fn:checkAndAdvance [checkAndAdvance at -1]} with state #queries(1)
+; GRAPH-NEXT: [AAFunctionReachability] for CtxI '  %2 = load i32, i32* %0, align 4' at position {fn:checkAndAdvance [checkAndAdvance at -1]} with state FunctionReachability [1,1]
 ; GRAPH-EMPTY:
-; GRAPH-NEXT: [AAIntraFnReachability] for CtxI ' %2 = load i32, i32* %0, align 4' at position {fn:checkAndAdvance [checkAndAdvance at -1]} with state #queries(1)
+; GRAPH-NEXT: [AACallEdges] for CtxI '  %2 = load i32, i32* %0, align 4' at position {fn:checkAndAdvance [checkAndAdvance at -1]} with state CallEdges[0,1]
 ; GRAPH-EMPTY:
 ; GRAPH-NEXT: [AACallEdges] for CtxI '  %6 = call i32* @checkAndAdvance(i32* %5)' at position {cs: [@-1]} with state CallEdges[0,1]
 ; GRAPH-EMPTY:
@@ -300,8 +300,8 @@ define i32* @checkAndAdvance(i32* align 16 %0) {
 ; DOT-DAG: Node[[Node26:0x[a-z0-9]+]] [shape=record,label="{[AAPotentialValues]
 ; DOT-DAG: Node[[Node27:0x[a-z0-9]+]] [shape=record,label="{[AAInstanceInfo]
 ; DOT-DAG: Node[[Node28:0x[a-z0-9]+]] [shape=record,label="{[AANoRecurse]
-; DOT-DAG: Node[[Node29:0x[a-z0-9]+]] [shape=record,label="{[AAInterFnReachability]
-; DOT-DAG: Node[[Node30:0x[a-z0-9]+]] [shape=record,label="{[AAIntraFnReachability]
+; DOT-DAG: Node[[Node29:0x[a-z0-9]+]] [shape=record,label="{[AAFunctionReachability]
+; DOT-DAG: Node[[Node30:0x[a-z0-9]+]] [shape=record,label="{[AACallEdges]
 ; DOT-DAG: Node[[Node31:0x[a-z0-9]+]] [shape=record,label="{[AACallEdges]
 ; DOT-DAG: Node[[Node32:0x[a-z0-9]+]] [shape=record,label="{[AAIsDead]
 ; DOT-DAG: Node[[Node33:0x[a-z0-9]+]] [shape=record,label="{[AAWillReturn]

diff  --git a/llvm/test/Transforms/Attributor/internal-noalias.ll b/llvm/test/Transforms/Attributor/internal-noalias.ll
index 2355660e1ee18..74608c93b3bb3 100644
--- a/llvm/test/Transforms/Attributor/internal-noalias.ll
+++ b/llvm/test/Transforms/Attributor/internal-noalias.ll
@@ -7,8 +7,8 @@ define dso_local i32 @visible(i32* noalias %A, i32* noalias %B) #0 {
 ; TUNIT-LABEL: define {{[^@]+}}@visible
 ; TUNIT-SAME: (i32* noalias nocapture nofree readonly [[A:%.*]], i32* noalias nocapture nofree readonly align 4 [[B:%.*]]) #[[ATTR0:[0-9]+]] {
 ; TUNIT-NEXT:  entry:
-; TUNIT-NEXT:    [[CALL1:%.*]] = call i32 @noalias_args(i32* noalias nocapture nofree readonly align 4 [[A]], i32* noalias nocapture nofree readonly align 4 [[B]]) #[[ATTR4:[0-9]+]]
-; TUNIT-NEXT:    [[CALL2:%.*]] = call i32 @noalias_args_argmem(i32* noalias nocapture nofree readonly align 4 [[A]], i32* noalias nocapture nofree readonly align 4 [[B]]) #[[ATTR4]]
+; TUNIT-NEXT:    [[CALL1:%.*]] = call i32 @noalias_args(i32* noalias nocapture nofree readonly align 4 [[A]], i32* noalias nocapture nofree readonly align 4 [[B]]) #[[ATTR3:[0-9]+]]
+; TUNIT-NEXT:    [[CALL2:%.*]] = call i32 @noalias_args_argmem(i32* noalias nocapture nofree readonly align 4 [[A]], i32* noalias nocapture nofree readonly align 4 [[B]]) #[[ATTR3]]
 ; TUNIT-NEXT:    [[ADD:%.*]] = add nsw i32 [[CALL1]], [[CALL2]]
 ; TUNIT-NEXT:    ret i32 [[ADD]]
 ;
@@ -36,7 +36,7 @@ define private i32 @noalias_args(i32* %A, i32* %B) #0 {
 ; TUNIT-NEXT:    [[TMP0:%.*]] = load i32, i32* [[A]], align 4
 ; TUNIT-NEXT:    [[TMP1:%.*]] = load i32, i32* [[B]], align 4
 ; TUNIT-NEXT:    [[ADD:%.*]] = add nsw i32 [[TMP0]], [[TMP1]]
-; TUNIT-NEXT:    [[CALL:%.*]] = call i32 @noalias_args_argmem(i32* nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[A]], i32* noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[B]]) #[[ATTR4]]
+; TUNIT-NEXT:    [[CALL:%.*]] = call i32 @noalias_args_argmem(i32* nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[A]], i32* noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[B]]) #[[ATTR3]]
 ; TUNIT-NEXT:    [[ADD2:%.*]] = add nsw i32 [[ADD]], [[CALL]]
 ; TUNIT-NEXT:    ret i32 [[ADD2]]
 ;
@@ -94,8 +94,8 @@ define dso_local i32 @visible_local(i32* %A) #0 {
 ; TUNIT-NEXT:  entry:
 ; TUNIT-NEXT:    [[B:%.*]] = alloca i32, align 4
 ; TUNIT-NEXT:    store i32 5, i32* [[B]], align 4
-; TUNIT-NEXT:    [[CALL1:%.*]] = call i32 @noalias_args(i32* nocapture nofree readonly align 4 [[A]], i32* noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[B]]) #[[ATTR4]]
-; TUNIT-NEXT:    [[CALL2:%.*]] = call i32 @noalias_args_argmem(i32* nocapture nofree readonly align 4 [[A]], i32* noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[B]]) #[[ATTR4]]
+; TUNIT-NEXT:    [[CALL1:%.*]] = call i32 @noalias_args(i32* nocapture nofree readonly align 4 [[A]], i32* noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[B]]) #[[ATTR3]]
+; TUNIT-NEXT:    [[CALL2:%.*]] = call i32 @noalias_args_argmem(i32* nocapture nofree readonly align 4 [[A]], i32* noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[B]]) #[[ATTR3]]
 ; TUNIT-NEXT:    [[ADD:%.*]] = add nsw i32 [[CALL1]], [[CALL2]]
 ; TUNIT-NEXT:    ret i32 [[ADD]]
 ;
@@ -158,10 +158,11 @@ define i32 @visible_local_2() {
 }
 
 define internal i32 @noalias_args_argmem_rn(i32* %A, i32* %B) #1 {
-; TUNIT: Function Attrs: nofree noinline norecurse nosync nounwind willreturn memory(argmem: write) uwtable
+; TUNIT: Function Attrs: nofree noinline norecurse nosync nounwind willreturn memory(argmem: readwrite) uwtable
 ; TUNIT-LABEL: define {{[^@]+}}@noalias_args_argmem_rn
-; TUNIT-SAME: (i32* noalias nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[B:%.*]]) #[[ATTR3:[0-9]+]] {
-; TUNIT-NEXT:    ret i32 undef
+; TUNIT-SAME: (i32* noalias nocapture nofree noundef nonnull align 4 dereferenceable(4) [[B:%.*]]) #[[ATTR1]] {
+; TUNIT-NEXT:    [[T0:%.*]] = load i32, i32* [[B]], align 4
+; TUNIT-NEXT:    ret i32 [[T0]]
 ;
 ; CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind willreturn memory(argmem: readwrite) uwtable
 ; CGSCC-LABEL: define {{[^@]+}}@noalias_args_argmem_rn
@@ -180,8 +181,9 @@ define i32 @visible_local_3() {
 ; TUNIT-LABEL: define {{[^@]+}}@visible_local_3
 ; TUNIT-SAME: () #[[ATTR2]] {
 ; TUNIT-NEXT:    [[B:%.*]] = alloca i32, align 4
-; TUNIT-NEXT:    [[CALL:%.*]] = call i32 @noalias_args_argmem_rn(i32* noalias nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[B]]) #[[ATTR5:[0-9]+]]
-; TUNIT-NEXT:    ret i32 5
+; TUNIT-NEXT:    store i32 5, i32* [[B]], align 4
+; TUNIT-NEXT:    [[CALL:%.*]] = call i32 @noalias_args_argmem_rn(i32* noalias nocapture nofree noundef nonnull align 4 dereferenceable(4) [[B]]) #[[ATTR4:[0-9]+]]
+; TUNIT-NEXT:    ret i32 [[CALL]]
 ;
 ; CGSCC: Function Attrs: nofree nosync nounwind willreturn memory(none)
 ; CGSCC-LABEL: define {{[^@]+}}@visible_local_3
@@ -203,9 +205,8 @@ attributes #1 = { argmemonly noinline nounwind uwtable willreturn}
 ; TUNIT: attributes #[[ATTR0]] = { nofree noinline norecurse nosync nounwind willreturn memory(argmem: read) uwtable }
 ; TUNIT: attributes #[[ATTR1]] = { nofree noinline norecurse nosync nounwind willreturn memory(argmem: readwrite) uwtable }
 ; TUNIT: attributes #[[ATTR2]] = { nofree norecurse nosync nounwind willreturn memory(none) }
-; TUNIT: attributes #[[ATTR3]] = { nofree noinline norecurse nosync nounwind willreturn memory(argmem: write) uwtable }
-; TUNIT: attributes #[[ATTR4]] = { nofree nosync nounwind }
-; TUNIT: attributes #[[ATTR5]] = { nofree nosync nounwind willreturn }
+; TUNIT: attributes #[[ATTR3]] = { nofree nosync nounwind }
+; TUNIT: attributes #[[ATTR4]] = { nofree nosync nounwind willreturn }
 ;.
 ; CGSCC: attributes #[[ATTR0]] = { nofree noinline nosync nounwind willreturn memory(argmem: read) uwtable }
 ; CGSCC: attributes #[[ATTR1]] = { nofree noinline norecurse nosync nounwind willreturn memory(argmem: read) uwtable }

diff  --git a/llvm/test/Transforms/Attributor/liveness_chains.ll b/llvm/test/Transforms/Attributor/liveness_chains.ll
index bca6434f43267..7e45148343d0a 100644
--- a/llvm/test/Transforms/Attributor/liveness_chains.ll
+++ b/llvm/test/Transforms/Attributor/liveness_chains.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
-; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
 ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
 
 ; Make sure we need a single iteration to determine the chains are dead/alive.

diff  --git a/llvm/test/Transforms/Attributor/lowerheap.ll b/llvm/test/Transforms/Attributor/lowerheap.ll
index d725df78f3016..0b10c1db35a9c 100644
--- a/llvm/test/Transforms/Attributor/lowerheap.ll
+++ b/llvm/test/Transforms/Attributor/lowerheap.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
-; RUN: opt -max-heap-to-stack-size=-1 -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
+; RUN: opt -max-heap-to-stack-size=-1 -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
 ; RUN: opt -max-heap-to-stack-size=-1 -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
 
 declare i64 @subfn(i8*) #0

diff  --git a/llvm/test/Transforms/Attributor/misc.ll b/llvm/test/Transforms/Attributor/misc.ll
index 5a5165e9d144c..44e7f41abb58a 100644
--- a/llvm/test/Transforms/Attributor/misc.ll
+++ b/llvm/test/Transforms/Attributor/misc.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
-; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=3 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
 ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
 ;
 ; Mostly check we do not crash on these uses

diff  --git a/llvm/test/Transforms/Attributor/noalias.ll b/llvm/test/Transforms/Attributor/noalias.ll
index 571038e5d76ce..dcb93fe46cc33 100644
--- a/llvm/test/Transforms/Attributor/noalias.ll
+++ b/llvm/test/Transforms/Attributor/noalias.ll
@@ -846,7 +846,7 @@ define void @test17_caller(i32* noalias %p, i32 %c) {
 ; TUNIT-NEXT:    tail call void @make_alias(i32* nofree writeonly [[P]]) #[[ATTR10]]
 ; TUNIT-NEXT:    br label [[L3:%.*]]
 ; TUNIT:       l2:
-; TUNIT-NEXT:    tail call void @only_store(i32* noalias nocapture nofree writeonly align 4 [[P]]) #[[ATTR10]]
+; TUNIT-NEXT:    tail call void @only_store(i32* nocapture nofree writeonly align 4 [[P]]) #[[ATTR10]]
 ; TUNIT-NEXT:    br label [[L3]]
 ; TUNIT:       l3:
 ; TUNIT-NEXT:    ret void

diff  --git a/llvm/test/Transforms/Attributor/noundef.ll b/llvm/test/Transforms/Attributor/noundef.ll
index 19a50a362acd3..9ff18cf716e8f 100644
--- a/llvm/test/Transforms/Attributor/noundef.ll
+++ b/llvm/test/Transforms/Attributor/noundef.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
-; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
 ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
 
 declare void @unknown()

diff  --git a/llvm/test/Transforms/Attributor/value-simplify-assume.ll b/llvm/test/Transforms/Attributor/value-simplify-assume.ll
index b9f6f27296e5a..aed444171f6d1 100644
--- a/llvm/test/Transforms/Attributor/value-simplify-assume.ll
+++ b/llvm/test/Transforms/Attributor/value-simplify-assume.ll
@@ -347,6 +347,7 @@ define i1 @assume_2_nr(i1 %arg, i1 %cond) norecurse {
 ; TUNIT-LABEL: define {{[^@]+}}@assume_2_nr
 ; TUNIT-SAME: (i1 [[ARG:%.*]], i1 [[COND:%.*]]) #[[ATTR3]] {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
+; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; TUNIT-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; TUNIT:       t:
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
@@ -363,6 +364,7 @@ define i1 @assume_2_nr(i1 %arg, i1 %cond) norecurse {
 ; CGSCC-LABEL: define {{[^@]+}}@assume_2_nr
 ; CGSCC-SAME: (i1 [[ARG:%.*]], i1 [[COND:%.*]]) #[[ATTR3]] {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
+; CGSCC-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; CGSCC-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; CGSCC:       t:
 ; CGSCC-NEXT:    store i1 true, i1* [[STACK]], align 1
@@ -422,6 +424,9 @@ define i1 @assume_3_nr(i1 %arg, i1 %cond) norecurse {
 ; TUNIT-LABEL: define {{[^@]+}}@assume_3_nr
 ; TUNIT-SAME: (i1 [[ARG:%.*]], i1 [[COND:%.*]]) #[[ATTR3]] {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
+; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
+; TUNIT-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; TUNIT:       t:
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
@@ -472,6 +477,7 @@ define i1 @assume_4_nr(i1 %arg, i1 %cond) norecurse {
 ; TUNIT-LABEL: define {{[^@]+}}@assume_4_nr
 ; TUNIT-SAME: (i1 [[ARG:%.*]], i1 [[COND:%.*]]) #[[ATTR3]] {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
+; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; TUNIT-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; TUNIT:       t:
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
@@ -524,6 +530,9 @@ define i1 @assume_5_nr(i1 %arg, i1 %cond) norecurse {
 ; TUNIT-LABEL: define {{[^@]+}}@assume_5_nr
 ; TUNIT-SAME: (i1 [[ARG:%.*]], i1 [[COND:%.*]]) #[[ATTR3]] {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
+; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
+; TUNIT-NEXT:    [[L1:%.*]] = load i1, i1* [[STACK]], align 1
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L1]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; TUNIT:       t:
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
@@ -592,7 +601,9 @@ define i1 @assume_5c_nr(i1 %cond) norecurse {
 ; TUNIT-LABEL: define {{[^@]+}}@assume_5c_nr
 ; TUNIT-SAME: (i1 [[COND:%.*]]) #[[ATTR3]] {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef true) #[[ATTR6]]
+; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
+; TUNIT-NEXT:    [[L1:%.*]] = load i1, i1* [[STACK]], align 1
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L1]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; TUNIT:       t:
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
@@ -962,6 +973,7 @@ define i1 @assume_2(i1 %arg, i1 %cond) {
 ; TUNIT-LABEL: define {{[^@]+}}@assume_2
 ; TUNIT-SAME: (i1 [[ARG:%.*]], i1 [[COND:%.*]]) #[[ATTR3]] {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
+; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; TUNIT-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; TUNIT:       t:
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
@@ -978,6 +990,7 @@ define i1 @assume_2(i1 %arg, i1 %cond) {
 ; CGSCC-LABEL: define {{[^@]+}}@assume_2
 ; CGSCC-SAME: (i1 [[ARG:%.*]], i1 [[COND:%.*]]) #[[ATTR3]] {
 ; CGSCC-NEXT:    [[STACK:%.*]] = alloca i1, align 1
+; CGSCC-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; CGSCC-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; CGSCC:       t:
 ; CGSCC-NEXT:    store i1 true, i1* [[STACK]], align 1
@@ -1037,6 +1050,9 @@ define i1 @assume_3(i1 %arg, i1 %cond) {
 ; TUNIT-LABEL: define {{[^@]+}}@assume_3
 ; TUNIT-SAME: (i1 [[ARG:%.*]], i1 [[COND:%.*]]) #[[ATTR3]] {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
+; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
+; TUNIT-NEXT:    [[L:%.*]] = load i1, i1* [[STACK]], align 1
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; TUNIT:       t:
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
@@ -1087,6 +1103,7 @@ define i1 @assume_4(i1 %arg, i1 %cond) {
 ; TUNIT-LABEL: define {{[^@]+}}@assume_4
 ; TUNIT-SAME: (i1 [[ARG:%.*]], i1 [[COND:%.*]]) #[[ATTR3]] {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
+; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
 ; TUNIT-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; TUNIT:       t:
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
@@ -1139,6 +1156,9 @@ define i1 @assume_5(i1 %arg, i1 %cond) {
 ; TUNIT-LABEL: define {{[^@]+}}@assume_5
 ; TUNIT-SAME: (i1 [[ARG:%.*]], i1 [[COND:%.*]]) #[[ATTR3]] {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
+; TUNIT-NEXT:    store i1 [[ARG]], i1* [[STACK]], align 1
+; TUNIT-NEXT:    [[L1:%.*]] = load i1, i1* [[STACK]], align 1
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L1]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; TUNIT:       t:
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
@@ -1207,7 +1227,9 @@ define i1 @assume_5c(i1 %cond) {
 ; TUNIT-LABEL: define {{[^@]+}}@assume_5c
 ; TUNIT-SAME: (i1 [[COND:%.*]]) #[[ATTR3]] {
 ; TUNIT-NEXT:    [[STACK:%.*]] = alloca i1, align 1
-; TUNIT-NEXT:    call void @llvm.assume(i1 noundef true) #[[ATTR6]]
+; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
+; TUNIT-NEXT:    [[L1:%.*]] = load i1, i1* [[STACK]], align 1
+; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[L1]]) #[[ATTR6]]
 ; TUNIT-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[F:%.*]]
 ; TUNIT:       t:
 ; TUNIT-NEXT:    store i1 true, i1* [[STACK]], align 1
@@ -1281,6 +1303,7 @@ define i32 @assume_read_global_good() {
 ; TUNIT-NEXT:    [[C:%.*]] = icmp eq i32 [[LGS1]], 42
 ; TUNIT-NEXT:    call void @llvm.assume(i1 noundef [[C]]) #[[ATTR6]]
 ; TUNIT-NEXT:    [[LGS2:%.*]] = load i32, i32* @Gstatic_int1, align 4
+; TUNIT-NEXT:    store i32 13, i32* @Gstatic_int1, align 4
 ; TUNIT-NEXT:    store i32 17, i32* @Gstatic_int1, align 4
 ; TUNIT-NEXT:    [[LGS3:%.*]] = load i32, i32* @Gstatic_int1, align 4
 ; TUNIT-NEXT:    [[ADD:%.*]] = add i32 [[LGS2]], [[LGS3]]
@@ -1293,6 +1316,7 @@ define i32 @assume_read_global_good() {
 ; CGSCC-NEXT:    [[C:%.*]] = icmp eq i32 [[LGS1]], 42
 ; CGSCC-NEXT:    call void @llvm.assume(i1 noundef [[C]]) #[[ATTR7]]
 ; CGSCC-NEXT:    [[LGS2:%.*]] = load i32, i32* @Gstatic_int1, align 4
+; CGSCC-NEXT:    store i32 13, i32* @Gstatic_int1, align 4
 ; CGSCC-NEXT:    store i32 17, i32* @Gstatic_int1, align 4
 ; CGSCC-NEXT:    [[LGS3:%.*]] = load i32, i32* @Gstatic_int1, align 4
 ; CGSCC-NEXT:    [[ADD:%.*]] = add i32 [[LGS2]], [[LGS3]]

diff  --git a/llvm/test/Transforms/Attributor/value-simplify-pointer-info.ll b/llvm/test/Transforms/Attributor/value-simplify-pointer-info.ll
index b8ac6e4c9fe2a..97a94cf2cd97b 100644
--- a/llvm/test/Transforms/Attributor/value-simplify-pointer-info.ll
+++ b/llvm/test/Transforms/Attributor/value-simplify-pointer-info.ll
@@ -542,9 +542,13 @@ define i32 @local_alloca_simplifiable_3() {
 ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define {{[^@]+}}@local_alloca_simplifiable_3
 ; CHECK-SAME: () #[[ATTR4:[0-9]+]] {
+; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    store i32 1, i32* [[A]], align 4
 ; CHECK-NEXT:    br label [[SPLIT:%.*]]
 ; CHECK:       split:
-; CHECK-NEXT:    ret i32 2
+; CHECK-NEXT:    store i32 2, i32* [[A]], align 4
+; CHECK-NEXT:    [[L:%.*]] = load i32, i32* [[A]], align 4
+; CHECK-NEXT:    ret i32 [[L]]
 ;
   %A = alloca i32, align 4
   store i32 1, i32* %A

diff  --git a/llvm/test/Transforms/Attributor/value-simplify-reachability.ll b/llvm/test/Transforms/Attributor/value-simplify-reachability.ll
deleted file mode 100644
index e1353511705d1..0000000000000
--- a/llvm/test/Transforms/Attributor/value-simplify-reachability.ll
+++ /dev/null
@@ -1,817 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
-; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=11 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
-; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
-
- at GInt1 = internal global i32 undef, align 4
- at GInt2 = internal global i32 zeroinitializer, align 4
- at GInt3 = internal global i32 undef, align 4
- at GInt4 = internal global i32 zeroinitializer, align 4
- at GInt5 = internal global i32 undef, align 4
-
-declare void @llvm.assume(i1)
-declare void @useI32(i32) nosync nocallback
-declare void @free(ptr) allockind("free") "alloc-family"="malloc"
-declare noalias ptr @calloc(i64, i64) allockind("alloc,zeroed") allocsize(0, 1) "alloc-family"="malloc"
-
-;.
-; CHECK: @[[GINT1:[a-zA-Z0-9_$"\\.-]+]] = internal global i32 undef, align 4
-; CHECK: @[[GINT2:[a-zA-Z0-9_$"\\.-]+]] = internal global i32 0, align 4
-; CHECK: @[[GINT3:[a-zA-Z0-9_$"\\.-]+]] = internal global i32 undef, align 4
-; CHECK: @[[GINT4:[a-zA-Z0-9_$"\\.-]+]] = internal global i32 0, align 4
-; CHECK: @[[GINT5:[a-zA-Z0-9_$"\\.-]+]] = internal global i32 undef, align 4
-;.
-define internal void @write1ToGInt1() {
-; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(write)
-; CHECK-LABEL: define {{[^@]+}}@write1ToGInt1
-; CHECK-SAME: () #[[ATTR4:[0-9]+]] {
-; CHECK-NEXT:    store i32 1, ptr @GInt1, align 4
-; CHECK-NEXT:    ret void
-;
-  store i32 1, ptr @GInt1
-  ret void
-}
-
-define internal void @write1ToGInt2() {
-; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(write)
-; CHECK-LABEL: define {{[^@]+}}@write1ToGInt2
-; CHECK-SAME: () #[[ATTR4]] {
-; CHECK-NEXT:    store i32 1, ptr @GInt2, align 4
-; CHECK-NEXT:    ret void
-;
-  store i32 1, ptr @GInt2
-  ret void
-}
-
-define void @entry1(i1 %c, i32 %v) {
-; TUNIT: Function Attrs: norecurse nosync
-; TUNIT-LABEL: define {{[^@]+}}@entry1
-; TUNIT-SAME: (i1 [[C:%.*]], i32 [[V:%.*]]) #[[ATTR5:[0-9]+]] {
-; TUNIT-NEXT:    [[L0:%.*]] = load i32, ptr @GInt1, align 4
-; TUNIT-NEXT:    call void @useI32(i32 [[L0]])
-; TUNIT-NEXT:    call void @write1ToGInt1() #[[ATTR10:[0-9]+]]
-; TUNIT-NEXT:    [[L1:%.*]] = load i32, ptr @GInt1, align 4
-; TUNIT-NEXT:    call void @useI32(i32 [[L1]])
-; TUNIT-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
-; TUNIT:       T:
-; TUNIT-NEXT:    store i32 [[V]], ptr @GInt1, align 4
-; TUNIT-NEXT:    [[L2:%.*]] = load i32, ptr @GInt1, align 4
-; TUNIT-NEXT:    call void @useI32(i32 [[L2]])
-; TUNIT-NEXT:    br label [[F]]
-; TUNIT:       F:
-; TUNIT-NEXT:    [[L3:%.*]] = load i32, ptr @GInt1, align 4
-; TUNIT-NEXT:    call void @useI32(i32 [[L3]])
-; TUNIT-NEXT:    call void @write1ToGInt1() #[[ATTR10]]
-; TUNIT-NEXT:    [[L4:%.*]] = load i32, ptr @GInt1, align 4
-; TUNIT-NEXT:    call void @useI32(i32 [[L4]])
-; TUNIT-NEXT:    ret void
-;
-; CGSCC: Function Attrs: nosync
-; CGSCC-LABEL: define {{[^@]+}}@entry1
-; CGSCC-SAME: (i1 [[C:%.*]], i32 [[V:%.*]]) #[[ATTR5:[0-9]+]] {
-; CGSCC-NEXT:    [[L0:%.*]] = load i32, ptr @GInt1, align 4
-; CGSCC-NEXT:    call void @useI32(i32 [[L0]])
-; CGSCC-NEXT:    call void @write1ToGInt1() #[[ATTR10:[0-9]+]]
-; CGSCC-NEXT:    [[L1:%.*]] = load i32, ptr @GInt1, align 4
-; CGSCC-NEXT:    call void @useI32(i32 [[L1]])
-; CGSCC-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
-; CGSCC:       T:
-; CGSCC-NEXT:    store i32 [[V]], ptr @GInt1, align 4
-; CGSCC-NEXT:    [[L2:%.*]] = load i32, ptr @GInt1, align 4
-; CGSCC-NEXT:    call void @useI32(i32 [[L2]])
-; CGSCC-NEXT:    br label [[F]]
-; CGSCC:       F:
-; CGSCC-NEXT:    [[L3:%.*]] = load i32, ptr @GInt1, align 4
-; CGSCC-NEXT:    call void @useI32(i32 [[L3]])
-; CGSCC-NEXT:    call void @write1ToGInt1() #[[ATTR10]]
-; CGSCC-NEXT:    [[L4:%.*]] = load i32, ptr @GInt1, align 4
-; CGSCC-NEXT:    call void @useI32(i32 [[L4]])
-; CGSCC-NEXT:    ret void
-;
-  %l0 = load i32, ptr @GInt1
-  call void @useI32(i32 %l0)
-  call void @write1ToGInt1();
-  %l1 = load i32, ptr @GInt1
-  call void @useI32(i32 %l1)
-  br i1 %c, label %T, label %F
-T:
-  store i32 %v, ptr @GInt1
-  %l2 = load i32, ptr @GInt1
-  call void @useI32(i32 %l2)
-  br label %F
-F:
-  %l3 = load i32, ptr @GInt1
-  call void @useI32(i32 %l3)
-  call void @write1ToGInt1();
-  %l4 = load i32, ptr @GInt1
-  call void @useI32(i32 %l4)
-  ret void
-}
-
-define void @entry2(i1 %c, i32 %v) {
-; TUNIT: Function Attrs: norecurse nosync
-; TUNIT-LABEL: define {{[^@]+}}@entry2
-; TUNIT-SAME: (i1 [[C:%.*]], i32 [[V:%.*]]) #[[ATTR5]] {
-; TUNIT-NEXT:    [[L0:%.*]] = load i32, ptr @GInt2, align 4
-; TUNIT-NEXT:    call void @useI32(i32 [[L0]])
-; TUNIT-NEXT:    call void @write1ToGInt2() #[[ATTR10]]
-; TUNIT-NEXT:    [[L1:%.*]] = load i32, ptr @GInt2, align 4
-; TUNIT-NEXT:    call void @useI32(i32 [[L1]])
-; TUNIT-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
-; TUNIT:       T:
-; TUNIT-NEXT:    store i32 [[V]], ptr @GInt2, align 4
-; TUNIT-NEXT:    [[L2:%.*]] = load i32, ptr @GInt2, align 4
-; TUNIT-NEXT:    call void @useI32(i32 [[L2]])
-; TUNIT-NEXT:    br label [[F]]
-; TUNIT:       F:
-; TUNIT-NEXT:    [[L3:%.*]] = load i32, ptr @GInt2, align 4
-; TUNIT-NEXT:    call void @useI32(i32 [[L3]])
-; TUNIT-NEXT:    call void @write1ToGInt2() #[[ATTR10]]
-; TUNIT-NEXT:    [[L4:%.*]] = load i32, ptr @GInt2, align 4
-; TUNIT-NEXT:    call void @useI32(i32 [[L4]])
-; TUNIT-NEXT:    ret void
-;
-; CGSCC: Function Attrs: nosync
-; CGSCC-LABEL: define {{[^@]+}}@entry2
-; CGSCC-SAME: (i1 [[C:%.*]], i32 [[V:%.*]]) #[[ATTR5]] {
-; CGSCC-NEXT:    [[L0:%.*]] = load i32, ptr @GInt2, align 4
-; CGSCC-NEXT:    call void @useI32(i32 [[L0]])
-; CGSCC-NEXT:    call void @write1ToGInt2() #[[ATTR10]]
-; CGSCC-NEXT:    [[L1:%.*]] = load i32, ptr @GInt2, align 4
-; CGSCC-NEXT:    call void @useI32(i32 [[L1]])
-; CGSCC-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
-; CGSCC:       T:
-; CGSCC-NEXT:    store i32 [[V]], ptr @GInt2, align 4
-; CGSCC-NEXT:    [[L2:%.*]] = load i32, ptr @GInt2, align 4
-; CGSCC-NEXT:    call void @useI32(i32 [[L2]])
-; CGSCC-NEXT:    br label [[F]]
-; CGSCC:       F:
-; CGSCC-NEXT:    [[L3:%.*]] = load i32, ptr @GInt2, align 4
-; CGSCC-NEXT:    call void @useI32(i32 [[L3]])
-; CGSCC-NEXT:    call void @write1ToGInt2() #[[ATTR10]]
-; CGSCC-NEXT:    [[L4:%.*]] = load i32, ptr @GInt2, align 4
-; CGSCC-NEXT:    call void @useI32(i32 [[L4]])
-; CGSCC-NEXT:    ret void
-;
-  %l0 = load i32, ptr @GInt2
-  call void @useI32(i32 %l0)
-  call void @write1ToGInt2();
-  %l1 = load i32, ptr @GInt2
-  call void @useI32(i32 %l1)
-  br i1 %c, label %T, label %F
-T:
-  store i32 %v, ptr @GInt2
-  %l2 = load i32, ptr @GInt2
-  call void @useI32(i32 %l2)
-  br label %F
-F:
-  %l3 = load i32, ptr @GInt2
-  call void @useI32(i32 %l3)
-  call void @write1ToGInt2();
-  %l4 = load i32, ptr @GInt2
-  call void @useI32(i32 %l4)
-  ret void
-}
-define void @entry3(i1 %c, i32 %v) {
-; TUNIT: Function Attrs: norecurse nosync
-; TUNIT-LABEL: define {{[^@]+}}@entry3
-; TUNIT-SAME: (i1 [[C:%.*]], i32 [[V:%.*]]) #[[ATTR5]] {
-; TUNIT-NEXT:    call void @useI32(i32 1)
-; TUNIT-NEXT:    store i32 1, ptr @GInt3, align 4
-; TUNIT-NEXT:    call void @useI32(i32 noundef 1)
-; TUNIT-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
-; TUNIT:       T:
-; TUNIT-NEXT:    store i32 [[V]], ptr @GInt3, align 4
-; TUNIT-NEXT:    [[L2:%.*]] = load i32, ptr @GInt3, align 4
-; TUNIT-NEXT:    call void @useI32(i32 [[L2]])
-; TUNIT-NEXT:    br label [[F]]
-; TUNIT:       F:
-; TUNIT-NEXT:    [[L3:%.*]] = load i32, ptr @GInt3, align 4
-; TUNIT-NEXT:    call void @useI32(i32 [[L3]])
-; TUNIT-NEXT:    store i32 1, ptr @GInt3, align 4
-; TUNIT-NEXT:    call void @useI32(i32 noundef 1)
-; TUNIT-NEXT:    ret void
-;
-; CGSCC: Function Attrs: norecurse nosync
-; CGSCC-LABEL: define {{[^@]+}}@entry3
-; CGSCC-SAME: (i1 [[C:%.*]], i32 [[V:%.*]]) #[[ATTR6:[0-9]+]] {
-; CGSCC-NEXT:    call void @useI32(i32 1)
-; CGSCC-NEXT:    store i32 1, ptr @GInt3, align 4
-; CGSCC-NEXT:    call void @useI32(i32 noundef 1)
-; CGSCC-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
-; CGSCC:       T:
-; CGSCC-NEXT:    store i32 [[V]], ptr @GInt3, align 4
-; CGSCC-NEXT:    [[L2:%.*]] = load i32, ptr @GInt3, align 4
-; CGSCC-NEXT:    call void @useI32(i32 [[L2]])
-; CGSCC-NEXT:    br label [[F]]
-; CGSCC:       F:
-; CGSCC-NEXT:    [[L3:%.*]] = load i32, ptr @GInt3, align 4
-; CGSCC-NEXT:    call void @useI32(i32 [[L3]])
-; CGSCC-NEXT:    store i32 1, ptr @GInt3, align 4
-; CGSCC-NEXT:    call void @useI32(i32 noundef 1)
-; CGSCC-NEXT:    ret void
-;
-  %l0 = load i32, ptr @GInt3
-  call void @useI32(i32 %l0)
-  store i32 1, ptr @GInt3
-  %l1 = load i32, ptr @GInt3
-  call void @useI32(i32 %l1)
-  br i1 %c, label %T, label %F
-T:
-  store i32 %v, ptr @GInt3
-  %l2 = load i32, ptr @GInt3
-  call void @useI32(i32 %l2)
-  br label %F
-F:
-  %l3 = load i32, ptr @GInt3
-  call void @useI32(i32 %l3)
-  store i32 1, ptr @GInt3
-  %l4 = load i32, ptr @GInt3
-  call void @useI32(i32 %l4)
-  ret void
-}
-
-define void @entry4(i1 %c, i32 %v) {
-; TUNIT: Function Attrs: norecurse nosync
-; TUNIT-LABEL: define {{[^@]+}}@entry4
-; TUNIT-SAME: (i1 [[C:%.*]], i32 [[V:%.*]]) #[[ATTR5]] {
-; TUNIT-NEXT:    [[L0:%.*]] = load i32, ptr @GInt4, align 4
-; TUNIT-NEXT:    call void @useI32(i32 noundef [[L0]])
-; TUNIT-NEXT:    store i32 1, ptr @GInt4, align 4
-; TUNIT-NEXT:    call void @useI32(i32 noundef 1)
-; TUNIT-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
-; TUNIT:       T:
-; TUNIT-NEXT:    store i32 [[V]], ptr @GInt4, align 4
-; TUNIT-NEXT:    [[L2:%.*]] = load i32, ptr @GInt4, align 4
-; TUNIT-NEXT:    call void @useI32(i32 [[L2]])
-; TUNIT-NEXT:    br label [[F]]
-; TUNIT:       F:
-; TUNIT-NEXT:    [[L3:%.*]] = load i32, ptr @GInt4, align 4
-; TUNIT-NEXT:    call void @useI32(i32 [[L3]])
-; TUNIT-NEXT:    store i32 1, ptr @GInt4, align 4
-; TUNIT-NEXT:    call void @useI32(i32 noundef 1)
-; TUNIT-NEXT:    ret void
-;
-; CGSCC: Function Attrs: norecurse nosync
-; CGSCC-LABEL: define {{[^@]+}}@entry4
-; CGSCC-SAME: (i1 [[C:%.*]], i32 [[V:%.*]]) #[[ATTR6]] {
-; CGSCC-NEXT:    [[L0:%.*]] = load i32, ptr @GInt4, align 4
-; CGSCC-NEXT:    call void @useI32(i32 noundef [[L0]])
-; CGSCC-NEXT:    store i32 1, ptr @GInt4, align 4
-; CGSCC-NEXT:    call void @useI32(i32 noundef 1)
-; CGSCC-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
-; CGSCC:       T:
-; CGSCC-NEXT:    store i32 [[V]], ptr @GInt4, align 4
-; CGSCC-NEXT:    [[L2:%.*]] = load i32, ptr @GInt4, align 4
-; CGSCC-NEXT:    call void @useI32(i32 [[L2]])
-; CGSCC-NEXT:    br label [[F]]
-; CGSCC:       F:
-; CGSCC-NEXT:    [[L3:%.*]] = load i32, ptr @GInt4, align 4
-; CGSCC-NEXT:    call void @useI32(i32 [[L3]])
-; CGSCC-NEXT:    store i32 1, ptr @GInt4, align 4
-; CGSCC-NEXT:    call void @useI32(i32 noundef 1)
-; CGSCC-NEXT:    ret void
-;
-  %l0 = load i32, ptr @GInt4
-  call void @useI32(i32 %l0)
-  store i32 1, ptr @GInt4
-  %l1 = load i32, ptr @GInt4
-  call void @useI32(i32 %l1)
-  br i1 %c, label %T, label %F
-T:
-  store i32 %v, ptr @GInt4
-  %l2 = load i32, ptr @GInt4
-  call void @useI32(i32 %l2)
-  br label %F
-F:
-  %l3 = load i32, ptr @GInt4
-  call void @useI32(i32 %l3)
-  store i32 1, ptr @GInt4
-  %l4 = load i32, ptr @GInt4
-  call void @useI32(i32 %l4)
-  ret void
-}
-
-; TODO: In this test we can replace %l0, in the others above we cannot.
-define void @entry5(i1 %c, i32 %v) {
-; TUNIT: Function Attrs: norecurse nosync
-; TUNIT-LABEL: define {{[^@]+}}@entry5
-; TUNIT-SAME: (i1 [[C:%.*]], i32 [[V:%.*]]) #[[ATTR5]] {
-; TUNIT-NEXT:    call void @useI32(i32 1)
-; TUNIT-NEXT:    store i32 1, ptr @GInt5, align 4
-; TUNIT-NEXT:    call void @useI32(i32 noundef 1) #[[ATTR6:[0-9]+]]
-; TUNIT-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
-; TUNIT:       T:
-; TUNIT-NEXT:    store i32 [[V]], ptr @GInt5, align 4
-; TUNIT-NEXT:    [[L2:%.*]] = load i32, ptr @GInt5, align 4
-; TUNIT-NEXT:    call void @useI32(i32 [[L2]]) #[[ATTR6]]
-; TUNIT-NEXT:    br label [[F]]
-; TUNIT:       F:
-; TUNIT-NEXT:    [[L3:%.*]] = load i32, ptr @GInt5, align 4
-; TUNIT-NEXT:    call void @useI32(i32 [[L3]]) #[[ATTR6]]
-; TUNIT-NEXT:    store i32 1, ptr @GInt5, align 4
-; TUNIT-NEXT:    call void @useI32(i32 noundef 1) #[[ATTR6]]
-; TUNIT-NEXT:    ret void
-;
-; CGSCC: Function Attrs: norecurse nosync
-; CGSCC-LABEL: define {{[^@]+}}@entry5
-; CGSCC-SAME: (i1 [[C:%.*]], i32 [[V:%.*]]) #[[ATTR6]] {
-; CGSCC-NEXT:    call void @useI32(i32 1)
-; CGSCC-NEXT:    store i32 1, ptr @GInt5, align 4
-; CGSCC-NEXT:    call void @useI32(i32 noundef 1) #[[ATTR7:[0-9]+]]
-; CGSCC-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
-; CGSCC:       T:
-; CGSCC-NEXT:    store i32 [[V]], ptr @GInt5, align 4
-; CGSCC-NEXT:    [[L2:%.*]] = load i32, ptr @GInt5, align 4
-; CGSCC-NEXT:    call void @useI32(i32 [[L2]]) #[[ATTR7]]
-; CGSCC-NEXT:    br label [[F]]
-; CGSCC:       F:
-; CGSCC-NEXT:    [[L3:%.*]] = load i32, ptr @GInt5, align 4
-; CGSCC-NEXT:    call void @useI32(i32 [[L3]]) #[[ATTR7]]
-; CGSCC-NEXT:    store i32 1, ptr @GInt5, align 4
-; CGSCC-NEXT:    call void @useI32(i32 noundef 1) #[[ATTR7]]
-; CGSCC-NEXT:    ret void
-;
-  %l0 = load i32, ptr @GInt5
-  call void @useI32(i32 %l0)
-  store i32 1, ptr @GInt5
-  %l1 = load i32, ptr @GInt5
-  call void @useI32(i32 %l1) nocallback
-  br i1 %c, label %T, label %F
-T:
-  store i32 %v, ptr @GInt5
-  %l2 = load i32, ptr @GInt5
-  call void @useI32(i32 %l2) nocallback
-  br label %F
-F:
-  %l3 = load i32, ptr @GInt5
-  call void @useI32(i32 %l3) nocallback
-  store i32 1, ptr @GInt5
-  %l4 = load i32, ptr @GInt5
-  call void @useI32(i32 %l4) nocallback
-  ret void
-}
-
-
-declare void @use_4_i8(i8, i8, i8, i8) nocallback
-
-define void @exclusion_set1(i1 %c1, i1 %c2, i1 %c3) {
-; CHECK-LABEL: define {{[^@]+}}@exclusion_set1
-; CHECK-SAME: (i1 [[C1:%.*]], i1 [[C2:%.*]], i1 [[C3:%.*]]) {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[CALL_H2S:%.*]] = alloca i8, i64 4, align 1
-; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr [[CALL_H2S]], i8 0, i64 4, i1 false)
-; CHECK-NEXT:    [[GEP1:%.*]] = getelementptr inbounds i8, ptr [[CALL_H2S]], i64 1
-; CHECK-NEXT:    [[GEP2:%.*]] = getelementptr inbounds i8, ptr [[CALL_H2S]], i64 2
-; CHECK-NEXT:    [[GEP3:%.*]] = getelementptr inbounds i8, ptr [[CALL_H2S]], i64 3
-; CHECK-NEXT:    [[L0_A:%.*]] = load i8, ptr [[CALL_H2S]], align 1
-; CHECK-NEXT:    [[L1_A:%.*]] = load i8, ptr [[GEP1]], align 1
-; CHECK-NEXT:    [[L2_A:%.*]] = load i8, ptr [[GEP2]], align 1
-; CHECK-NEXT:    [[L3_A:%.*]] = load i8, ptr [[GEP3]], align 1
-; CHECK-NEXT:    call void @use_4_i8(i8 noundef [[L0_A]], i8 noundef [[L1_A]], i8 noundef [[L2_A]], i8 noundef [[L3_A]])
-; CHECK-NEXT:    store i8 1, ptr [[CALL_H2S]], align 4
-; CHECK-NEXT:    [[L1_B:%.*]] = load i8, ptr [[GEP1]], align 1
-; CHECK-NEXT:    [[L2_B:%.*]] = load i8, ptr [[GEP2]], align 1
-; CHECK-NEXT:    [[L3_B:%.*]] = load i8, ptr [[GEP3]], align 1
-; CHECK-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef [[L1_B]], i8 noundef [[L2_B]], i8 noundef [[L3_B]])
-; CHECK-NEXT:    br i1 [[C1]], label [[IF_MERGE1:%.*]], label [[IF_THEN:%.*]]
-; CHECK:       if.then:
-; CHECK-NEXT:    [[L1_C:%.*]] = load i8, ptr [[GEP1]], align 1
-; CHECK-NEXT:    [[L2_C:%.*]] = load i8, ptr [[GEP2]], align 1
-; CHECK-NEXT:    [[L3_C:%.*]] = load i8, ptr [[GEP3]], align 1
-; CHECK-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef [[L1_C]], i8 noundef [[L2_C]], i8 noundef [[L3_C]])
-; CHECK-NEXT:    store i8 2, ptr [[GEP1]], align 4
-; CHECK-NEXT:    [[L2_D:%.*]] = load i8, ptr [[GEP2]], align 1
-; CHECK-NEXT:    [[L3_D:%.*]] = load i8, ptr [[GEP3]], align 1
-; CHECK-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef [[L2_D]], i8 noundef [[L3_D]])
-; CHECK-NEXT:    br i1 [[C1]], label [[IF_MERGE1]], label [[IF_THEN2:%.*]]
-; CHECK:       if.then2:
-; CHECK-NEXT:    [[L2_E:%.*]] = load i8, ptr [[GEP2]], align 1
-; CHECK-NEXT:    [[L3_E:%.*]] = load i8, ptr [[GEP3]], align 1
-; CHECK-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef [[L2_E]], i8 noundef [[L3_E]])
-; CHECK-NEXT:    store i8 3, ptr [[GEP2]], align 4
-; CHECK-NEXT:    [[L3_F:%.*]] = load i8, ptr [[GEP3]], align 1
-; CHECK-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef 3, i8 noundef [[L3_F]])
-; CHECK-NEXT:    br i1 [[C2]], label [[IF_MERGE2:%.*]], label [[IF_THEN3:%.*]]
-; CHECK:       if.merge1:
-; CHECK-NEXT:    [[L1_G:%.*]] = load i8, ptr [[GEP1]], align 1
-; CHECK-NEXT:    [[L2_G:%.*]] = load i8, ptr [[GEP2]], align 1
-; CHECK-NEXT:    [[L3_G:%.*]] = load i8, ptr [[GEP3]], align 1
-; CHECK-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef [[L1_G]], i8 noundef [[L2_G]], i8 noundef [[L3_G]])
-; CHECK-NEXT:    br label [[IF_MERGE2]]
-; CHECK:       if.merge2:
-; CHECK-NEXT:    [[L1_H:%.*]] = load i8, ptr [[GEP1]], align 1
-; CHECK-NEXT:    [[L2_H:%.*]] = load i8, ptr [[GEP2]], align 1
-; CHECK-NEXT:    [[L3_H:%.*]] = load i8, ptr [[GEP3]], align 1
-; CHECK-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef [[L1_H]], i8 noundef [[L2_H]], i8 noundef [[L3_H]])
-; CHECK-NEXT:    br label [[IF_END:%.*]]
-; CHECK:       if.then3:
-; CHECK-NEXT:    [[L3_I:%.*]] = load i8, ptr [[GEP3]], align 1
-; CHECK-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef 3, i8 noundef [[L3_I]])
-; CHECK-NEXT:    store i8 4, ptr [[GEP3]], align 4
-; CHECK-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef 3, i8 noundef 4)
-; CHECK-NEXT:    br label [[IF_END]]
-; CHECK:       if.end:
-; CHECK-NEXT:    [[L1_K:%.*]] = load i8, ptr [[GEP1]], align 1
-; CHECK-NEXT:    [[L2_K:%.*]] = load i8, ptr [[GEP2]], align 1
-; CHECK-NEXT:    [[L3_K:%.*]] = load i8, ptr [[GEP3]], align 1
-; CHECK-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef [[L1_K]], i8 noundef [[L2_K]], i8 noundef [[L3_K]])
-; CHECK-NEXT:    ret void
-;
-entry:
-  %call = call noalias i8* @calloc(i64 1, i64 4) norecurse
-  %gep0 = getelementptr inbounds i8, i8* %call, i64 0
-  %gep1 = getelementptr inbounds i8, i8* %call, i64 1
-  %gep2 = getelementptr inbounds i8, i8* %call, i64 2
-  %gep3 = getelementptr inbounds i8, i8* %call, i64 3
-
-  %l0_a = load i8, i8* %gep0
-  %l1_a = load i8, i8* %gep1
-  %l2_a = load i8, i8* %gep2
-  %l3_a = load i8, i8* %gep3
-  call void @use_4_i8(i8 %l0_a, i8 %l1_a, i8 %l2_a, i8 %l3_a)
-
-  store i8 1, i8* %gep0, align 4
-
-  %l0_b = load i8, i8* %gep0
-  %l1_b = load i8, i8* %gep1
-  %l2_b = load i8, i8* %gep2
-  %l3_b = load i8, i8* %gep3
-  call void @use_4_i8(i8 %l0_b, i8 %l1_b, i8 %l2_b, i8 %l3_b)
-
-  br i1 %c1, label %if.merge1, label %if.then
-
-if.then:
-  %l0_c = load i8, i8* %gep0
-  %l1_c = load i8, i8* %gep1
-  %l2_c = load i8, i8* %gep2
-  %l3_c = load i8, i8* %gep3
-  call void @use_4_i8(i8 %l0_c, i8 %l1_c, i8 %l2_c, i8 %l3_c)
-
-  store i8 2, i8* %gep1, align 4
-
-  %l0_d = load i8, i8* %gep0
-  %l1_d = load i8, i8* %gep1
-  %l2_d = load i8, i8* %gep2
-  %l3_d = load i8, i8* %gep3
-  call void @use_4_i8(i8 %l0_d, i8 %l1_d, i8 %l2_d, i8 %l3_d)
-
-  br i1 %c1, label %if.merge1, label %if.then2
-
-if.then2:
-  %l0_e = load i8, i8* %gep0
-  %l1_e = load i8, i8* %gep1
-  %l2_e = load i8, i8* %gep2
-  %l3_e = load i8, i8* %gep3
-  call void @use_4_i8(i8 %l0_e, i8 %l1_e, i8 %l2_e, i8 %l3_e)
-
-  store i8 3, i8* %gep2, align 4
-
-  %l0_f = load i8, i8* %gep0
-  %l1_f = load i8, i8* %gep1
-  %l2_f = load i8, i8* %gep2
-  %l3_f = load i8, i8* %gep3
-  call void @use_4_i8(i8 %l0_f, i8 %l1_f, i8 %l2_f, i8 %l3_f)
-
-  br i1 %c2, label %if.merge2, label %if.then3
-
-if.merge1:
-
-  %l0_g = load i8, i8* %gep0
-  %l1_g = load i8, i8* %gep1
-  %l2_g = load i8, i8* %gep2
-  %l3_g = load i8, i8* %gep3
-  call void @use_4_i8(i8 %l0_g, i8 %l1_g, i8 %l2_g, i8 %l3_g)
-
-  br label %if.merge2
-
-if.merge2:
-
-  %l0_h = load i8, i8* %gep0
-  %l1_h = load i8, i8* %gep1
-  %l2_h = load i8, i8* %gep2
-  %l3_h = load i8, i8* %gep3
-  call void @use_4_i8(i8 %l0_h, i8 %l1_h, i8 %l2_h, i8 %l3_h)
-
-  br label %if.end
-
-if.then3:
-
-  %l0_i = load i8, i8* %gep0
-  %l1_i = load i8, i8* %gep1
-  %l2_i = load i8, i8* %gep2
-  %l3_i = load i8, i8* %gep3
-  call void @use_4_i8(i8 %l0_i, i8 %l1_i, i8 %l2_i, i8 %l3_i)
-
-  store i8 4, i8* %gep3, align 4
-
-  %l0_j = load i8, i8* %gep0
-  %l1_j = load i8, i8* %gep1
-  %l2_j = load i8, i8* %gep2
-  %l3_j = load i8, i8* %gep3
-  call void @use_4_i8(i8 %l0_j, i8 %l1_j, i8 %l2_j, i8 %l3_j)
-
-  br label %if.end
-
-if.end:
-  %l0_k = load i8, i8* %gep0
-  %l1_k = load i8, i8* %gep1
-  %l2_k = load i8, i8* %gep2
-  %l3_k = load i8, i8* %gep3
-  call void @use_4_i8(i8 %l0_k, i8 %l1_k, i8 %l2_k, i8 %l3_k)
-
-  call void @free(i8* %call) norecurse
-  ret void
-}
-
-define void @exclusion_set2(i1 %c1, i1 %c2, i1 %c3) {
-; TUNIT: Function Attrs: norecurse
-; TUNIT-LABEL: define {{[^@]+}}@exclusion_set2
-; TUNIT-SAME: (i1 [[C1:%.*]], i1 [[C2:%.*]], i1 [[C3:%.*]]) #[[ATTR7:[0-9]+]] {
-; TUNIT-NEXT:  entry:
-; TUNIT-NEXT:    call void @use_4_i8(i8 1, i8 2, i8 3, i8 4)
-; TUNIT-NEXT:    call void @use_4_i8(i8 noundef 1, i8 2, i8 3, i8 4)
-; TUNIT-NEXT:    br i1 [[C1]], label [[IF_MERGE1:%.*]], label [[IF_THEN:%.*]]
-; TUNIT:       if.then:
-; TUNIT-NEXT:    call void @use_4_i8(i8 noundef 1, i8 2, i8 3, i8 4)
-; TUNIT-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 3, i8 4)
-; TUNIT-NEXT:    br i1 [[C1]], label [[IF_MERGE1]], label [[IF_THEN2:%.*]]
-; TUNIT:       if.then2:
-; TUNIT-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 3, i8 4)
-; TUNIT-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef 3, i8 4)
-; TUNIT-NEXT:    br i1 [[C2]], label [[IF_MERGE2:%.*]], label [[IF_THEN3:%.*]]
-; TUNIT:       if.merge1:
-; TUNIT-NEXT:    call void @use_4_i8(i8 noundef 1, i8 2, i8 3, i8 4)
-; TUNIT-NEXT:    br label [[IF_MERGE2]]
-; TUNIT:       if.merge2:
-; TUNIT-NEXT:    call void @use_4_i8(i8 noundef 1, i8 2, i8 3, i8 4)
-; TUNIT-NEXT:    br label [[IF_END:%.*]]
-; TUNIT:       if.then3:
-; TUNIT-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef 3, i8 4)
-; TUNIT-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef 3, i8 noundef 4)
-; TUNIT-NEXT:    br label [[IF_END]]
-; TUNIT:       if.end:
-; TUNIT-NEXT:    call void @use_4_i8(i8 noundef 1, i8 2, i8 3, i8 4)
-; TUNIT-NEXT:    ret void
-;
-; CGSCC: Function Attrs: norecurse
-; CGSCC-LABEL: define {{[^@]+}}@exclusion_set2
-; CGSCC-SAME: (i1 [[C1:%.*]], i1 [[C2:%.*]], i1 [[C3:%.*]]) #[[ATTR8:[0-9]+]] {
-; CGSCC-NEXT:  entry:
-; CGSCC-NEXT:    call void @use_4_i8(i8 1, i8 2, i8 3, i8 4)
-; CGSCC-NEXT:    call void @use_4_i8(i8 noundef 1, i8 2, i8 3, i8 4)
-; CGSCC-NEXT:    br i1 [[C1]], label [[IF_MERGE1:%.*]], label [[IF_THEN:%.*]]
-; CGSCC:       if.then:
-; CGSCC-NEXT:    call void @use_4_i8(i8 noundef 1, i8 2, i8 3, i8 4)
-; CGSCC-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 3, i8 4)
-; CGSCC-NEXT:    br i1 [[C1]], label [[IF_MERGE1]], label [[IF_THEN2:%.*]]
-; CGSCC:       if.then2:
-; CGSCC-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 3, i8 4)
-; CGSCC-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef 3, i8 4)
-; CGSCC-NEXT:    br i1 [[C2]], label [[IF_MERGE2:%.*]], label [[IF_THEN3:%.*]]
-; CGSCC:       if.merge1:
-; CGSCC-NEXT:    call void @use_4_i8(i8 noundef 1, i8 2, i8 3, i8 4)
-; CGSCC-NEXT:    br label [[IF_MERGE2]]
-; CGSCC:       if.merge2:
-; CGSCC-NEXT:    call void @use_4_i8(i8 noundef 1, i8 2, i8 3, i8 4)
-; CGSCC-NEXT:    br label [[IF_END:%.*]]
-; CGSCC:       if.then3:
-; CGSCC-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef 3, i8 4)
-; CGSCC-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef 3, i8 noundef 4)
-; CGSCC-NEXT:    br label [[IF_END]]
-; CGSCC:       if.end:
-; CGSCC-NEXT:    call void @use_4_i8(i8 noundef 1, i8 2, i8 3, i8 4)
-; CGSCC-NEXT:    ret void
-;
-entry:
-  %alloc = alloca i8, i32 4
-  %gep0 = getelementptr inbounds i8, ptr %alloc, i64 0
-  %gep1 = getelementptr inbounds i8, ptr %alloc, i64 1
-  %gep2 = getelementptr inbounds i8, ptr %alloc, i64 2
-  %gep3 = getelementptr inbounds i8, ptr %alloc, i64 3
-
-  %l0_a = load i8, ptr %gep0
-  %l1_a = load i8, ptr %gep1
-  %l2_a = load i8, ptr %gep2
-  %l3_a = load i8, ptr %gep3
-  call void @use_4_i8(i8 %l0_a, i8 %l1_a, i8 %l2_a, i8 %l3_a)
-
-  store i8 1, ptr %gep0, align 4
-
-  %l0_b = load i8, ptr %gep0
-  %l1_b = load i8, ptr %gep1
-  %l2_b = load i8, ptr %gep2
-  %l3_b = load i8, ptr %gep3
-  call void @use_4_i8(i8 %l0_b, i8 %l1_b, i8 %l2_b, i8 %l3_b)
-
-  br i1 %c1, label %if.merge1, label %if.then
-
-if.then:
-  %l0_c = load i8, ptr %gep0
-  %l1_c = load i8, ptr %gep1
-  %l2_c = load i8, ptr %gep2
-  %l3_c = load i8, ptr %gep3
-  call void @use_4_i8(i8 %l0_c, i8 %l1_c, i8 %l2_c, i8 %l3_c)
-
-  store i8 2, ptr %gep1, align 4
-
-  %l0_d = load i8, ptr %gep0
-  %l1_d = load i8, ptr %gep1
-  %l2_d = load i8, ptr %gep2
-  %l3_d = load i8, ptr %gep3
-  call void @use_4_i8(i8 %l0_d, i8 %l1_d, i8 %l2_d, i8 %l3_d)
-
-  br i1 %c1, label %if.merge1, label %if.then2
-
-if.then2:
-  %l0_e = load i8, ptr %gep0
-  %l1_e = load i8, ptr %gep1
-  %l2_e = load i8, ptr %gep2
-  %l3_e = load i8, ptr %gep3
-  call void @use_4_i8(i8 %l0_e, i8 %l1_e, i8 %l2_e, i8 %l3_e)
-
-  store i8 3, ptr %gep2, align 4
-
-  %l0_f = load i8, ptr %gep0
-  %l1_f = load i8, ptr %gep1
-  %l2_f = load i8, ptr %gep2
-  %l3_f = load i8, ptr %gep3
-  call void @use_4_i8(i8 %l0_f, i8 %l1_f, i8 %l2_f, i8 %l3_f)
-
-  br i1 %c2, label %if.merge2, label %if.then3
-
-if.merge1:
-
-  %l0_g = load i8, ptr %gep0
-  %l1_g = load i8, ptr %gep1
-  %l2_g = load i8, ptr %gep2
-  %l3_g = load i8, ptr %gep3
-  call void @use_4_i8(i8 %l0_g, i8 %l1_g, i8 %l2_g, i8 %l3_g)
-
-  br label %if.merge2
-
-if.merge2:
-
-  %l0_h = load i8, ptr %gep0
-  %l1_h = load i8, ptr %gep1
-  %l2_h = load i8, ptr %gep2
-  %l3_h = load i8, ptr %gep3
-  call void @use_4_i8(i8 %l0_h, i8 %l1_h, i8 %l2_h, i8 %l3_h)
-
-  br label %if.end
-
-if.then3:
-
-  %l0_i = load i8, ptr %gep0
-  %l1_i = load i8, ptr %gep1
-  %l2_i = load i8, ptr %gep2
-  %l3_i = load i8, ptr %gep3
-  call void @use_4_i8(i8 %l0_i, i8 %l1_i, i8 %l2_i, i8 %l3_i)
-
-  store i8 4, ptr %gep3, align 4
-
-  %l0_j = load i8, ptr %gep0
-  %l1_j = load i8, ptr %gep1
-  %l2_j = load i8, ptr %gep2
-  %l3_j = load i8, ptr %gep3
-  call void @use_4_i8(i8 %l0_j, i8 %l1_j, i8 %l2_j, i8 %l3_j)
-
-  br label %if.end
-
-if.end:
-  %l0_k = load i8, ptr %gep0
-  %l1_k = load i8, ptr %gep1
-  %l2_k = load i8, ptr %gep2
-  %l3_k = load i8, ptr %gep3
-  call void @use_4_i8(i8 %l0_k, i8 %l1_k, i8 %l2_k, i8 %l3_k)
-
-  ret void
-}
-
-declare void @usei32(i32) nocallback nosync
-define internal void @exclusion_set3_helper(i1 %c, ptr %p) {
-; TUNIT: Function Attrs: nosync
-; TUNIT-LABEL: define {{[^@]+}}@exclusion_set3_helper
-; TUNIT-SAME: (i1 [[C:%.*]], ptr noalias nocapture nofree noundef nonnull align 4 dereferenceable(4) [[P:%.*]]) #[[ATTR8:[0-9]+]] {
-; TUNIT-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
-; TUNIT:       t:
-; TUNIT-NEXT:    store i32 42, ptr [[P]], align 4
-; TUNIT-NEXT:    br label [[M:%.*]]
-; TUNIT:       f:
-; TUNIT-NEXT:    [[L:%.*]] = load i32, ptr [[P]], align 4
-; TUNIT-NEXT:    [[ADD:%.*]] = add i32 [[L]], 1
-; TUNIT-NEXT:    store i32 [[ADD]], ptr [[P]], align 4
-; TUNIT-NEXT:    [[CND:%.*]] = icmp eq i32 [[L]], 100
-; TUNIT-NEXT:    br i1 [[CND]], label [[F2:%.*]], label [[F]]
-; TUNIT:       f2:
-; TUNIT-NEXT:    [[USE1:%.*]] = load i32, ptr [[P]], align 4
-; TUNIT-NEXT:    call void @usei32(i32 [[USE1]])
-; TUNIT-NEXT:    store i32 77, ptr [[P]], align 4
-; TUNIT-NEXT:    call void @exclusion_set3_helper(i1 noundef true, ptr noalias nocapture nofree noundef nonnull align 4 dereferenceable(4) [[P]]) #[[ATTR8]]
-; TUNIT-NEXT:    [[USE2:%.*]] = load i32, ptr [[P]], align 4
-; TUNIT-NEXT:    call void @usei32(i32 [[USE2]])
-; TUNIT-NEXT:    br label [[T]]
-; TUNIT:       m:
-; TUNIT-NEXT:    call void @usei32(i32 42)
-; TUNIT-NEXT:    ret void
-;
-; CGSCC: Function Attrs: nosync
-; CGSCC-LABEL: define {{[^@]+}}@exclusion_set3_helper
-; CGSCC-SAME: (i1 [[C:%.*]], ptr noalias nocapture nofree noundef nonnull align 4 dereferenceable(4) [[P:%.*]]) #[[ATTR5]] {
-; CGSCC-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
-; CGSCC:       t:
-; CGSCC-NEXT:    store i32 42, ptr [[P]], align 4
-; CGSCC-NEXT:    br label [[M:%.*]]
-; CGSCC:       f:
-; CGSCC-NEXT:    [[L:%.*]] = load i32, ptr [[P]], align 4
-; CGSCC-NEXT:    [[ADD:%.*]] = add i32 [[L]], 1
-; CGSCC-NEXT:    store i32 [[ADD]], ptr [[P]], align 4
-; CGSCC-NEXT:    [[CND:%.*]] = icmp eq i32 [[L]], 100
-; CGSCC-NEXT:    br i1 [[CND]], label [[F2:%.*]], label [[F]]
-; CGSCC:       f2:
-; CGSCC-NEXT:    [[USE1:%.*]] = load i32, ptr [[P]], align 4
-; CGSCC-NEXT:    call void @usei32(i32 [[USE1]])
-; CGSCC-NEXT:    store i32 77, ptr [[P]], align 4
-; CGSCC-NEXT:    call void @exclusion_set3_helper(i1 noundef true, ptr noalias nocapture nofree noundef nonnull align 4 dereferenceable(4) [[P]]) #[[ATTR5]]
-; CGSCC-NEXT:    [[USE2:%.*]] = load i32, ptr [[P]], align 4
-; CGSCC-NEXT:    call void @usei32(i32 [[USE2]])
-; CGSCC-NEXT:    br label [[T]]
-; CGSCC:       m:
-; CGSCC-NEXT:    [[USE3:%.*]] = load i32, ptr [[P]], align 4
-; CGSCC-NEXT:    call void @usei32(i32 [[USE3]])
-; CGSCC-NEXT:    ret void
-;
-  br i1 %c, label %t, label %f
-t:
-  store i32 42, ptr %p
-  br label %m
-f:
-  %l = load i32, ptr %p
-  %add = add i32 %l, 1
-  store i32 %add, ptr %p
-  %cnd = icmp eq i32 %l, 100
-  br i1 %cnd, label %f2, label %f
-f2:
-  %use1 = load i32, ptr %p
-  call void @usei32(i32 %use1)
-  store i32 77, ptr %p
-  call void @exclusion_set3_helper(i1 true, ptr %p)
-  %use2 = load i32, ptr %p
-  call void @usei32(i32 %use2)
-  br label %t
-m:
-  %use3 = load i32, ptr %p
-  call void @usei32(i32 %use3)
-  ret void
-}
-
-define i32 @exclusion_set3(i1 %c) {
-; TUNIT: Function Attrs: norecurse nosync
-; TUNIT-LABEL: define {{[^@]+}}@exclusion_set3
-; TUNIT-SAME: (i1 [[C:%.*]]) #[[ATTR5]] {
-; TUNIT-NEXT:    [[A:%.*]] = alloca i32, align 4
-; TUNIT-NEXT:    store i32 3, ptr [[A]], align 4
-; TUNIT-NEXT:    call void @exclusion_set3_helper(i1 [[C]], ptr noalias nocapture nofree noundef nonnull align 4 dereferenceable(4) [[A]]) #[[ATTR8]]
-; TUNIT-NEXT:    [[FINAL:%.*]] = load i32, ptr [[A]], align 4
-; TUNIT-NEXT:    ret i32 [[FINAL]]
-;
-; CGSCC: Function Attrs: nosync
-; CGSCC-LABEL: define {{[^@]+}}@exclusion_set3
-; CGSCC-SAME: (i1 [[C:%.*]]) #[[ATTR5]] {
-; CGSCC-NEXT:    [[A:%.*]] = alloca i32, align 4
-; CGSCC-NEXT:    store i32 3, ptr [[A]], align 4
-; CGSCC-NEXT:    call void @exclusion_set3_helper(i1 [[C]], ptr noalias nocapture nofree noundef nonnull align 4 dereferenceable(4) [[A]])
-; CGSCC-NEXT:    [[FINAL:%.*]] = load i32, ptr [[A]], align 4
-; CGSCC-NEXT:    ret i32 [[FINAL]]
-;
-  %a = alloca i32
-  store i32 3, ptr %a
-  call void @exclusion_set3_helper(i1 %c, ptr %a)
-  %final = load i32, ptr %a
-  ret i32 %final
-}
-
-;.
-; TUNIT: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) }
-; TUNIT: attributes #[[ATTR1:[0-9]+]] = { nocallback nosync }
-; TUNIT: attributes #[[ATTR2:[0-9]+]] = { allockind("free") "alloc-family"="malloc" }
-; TUNIT: attributes #[[ATTR3:[0-9]+]] = { allockind("alloc,zeroed") allocsize(0,1) "alloc-family"="malloc" }
-; TUNIT: attributes #[[ATTR4]] = { nofree norecurse nosync nounwind willreturn memory(write) }
-; TUNIT: attributes #[[ATTR5]] = { norecurse nosync }
-; TUNIT: attributes #[[ATTR6]] = { nocallback }
-; TUNIT: attributes #[[ATTR7]] = { norecurse }
-; TUNIT: attributes #[[ATTR8]] = { nosync }
-; TUNIT: attributes #[[ATTR9:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: write) }
-; TUNIT: attributes #[[ATTR10]] = { nosync nounwind }
-;.
-; CGSCC: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) }
-; CGSCC: attributes #[[ATTR1:[0-9]+]] = { nocallback nosync }
-; CGSCC: attributes #[[ATTR2:[0-9]+]] = { allockind("free") "alloc-family"="malloc" }
-; CGSCC: attributes #[[ATTR3:[0-9]+]] = { allockind("alloc,zeroed") allocsize(0,1) "alloc-family"="malloc" }
-; CGSCC: attributes #[[ATTR4]] = { nofree norecurse nosync nounwind willreturn memory(write) }
-; CGSCC: attributes #[[ATTR5]] = { nosync }
-; CGSCC: attributes #[[ATTR6]] = { norecurse nosync }
-; CGSCC: attributes #[[ATTR7]] = { nocallback }
-; CGSCC: attributes #[[ATTR8]] = { norecurse }
-; CGSCC: attributes #[[ATTR9:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: write) }
-; CGSCC: attributes #[[ATTR10]] = { nounwind }
-;.

diff  --git a/llvm/unittests/Transforms/IPO/AttributorTest.cpp b/llvm/unittests/Transforms/IPO/AttributorTest.cpp
index 4a38684b0dbf7..cd289a1a2a353 100644
--- a/llvm/unittests/Transforms/IPO/AttributorTest.cpp
+++ b/llvm/unittests/Transforms/IPO/AttributorTest.cpp
@@ -169,23 +169,23 @@ TEST_F(AttributorTestBase, AAReachabilityTest) {
   // call void @func8
   Instruction &F9SecondInst = *++(F9.getEntryBlock().begin());
 
-  const AAInterFnReachability &F1AA =
-      A.getOrCreateAAFor<AAInterFnReachability>(IRPosition::function(F1));
+  const AAFunctionReachability &F1AA =
+      A.getOrCreateAAFor<AAFunctionReachability>(IRPosition::function(F1));
 
-  const AAInterFnReachability &F6AA =
-      A.getOrCreateAAFor<AAInterFnReachability>(IRPosition::function(F6));
+  const AAFunctionReachability &F6AA =
+      A.getOrCreateAAFor<AAFunctionReachability>(IRPosition::function(F6));
 
-  const AAInterFnReachability &F7AA =
-      A.getOrCreateAAFor<AAInterFnReachability>(IRPosition::function(F7));
+  const AAFunctionReachability &F7AA =
+      A.getOrCreateAAFor<AAFunctionReachability>(IRPosition::function(F7));
 
-  const AAInterFnReachability &F9AA =
-      A.getOrCreateAAFor<AAInterFnReachability>(IRPosition::function(F9));
+  const AAFunctionReachability &F9AA =
+      A.getOrCreateAAFor<AAFunctionReachability>(IRPosition::function(F9));
 
   F1AA.canReach(A, F3);
   F1AA.canReach(A, F4);
   F6AA.canReach(A, F4);
-  F7AA.instructionCanReach(A, F7FirstCB, F3);
-  F7AA.instructionCanReach(A, F7FirstCB, F4);
+  F7AA.canReach(A, F7FirstCB, F3);
+  F7AA.canReach(A, F7FirstCB, F4);
   F9AA.instructionCanReach(A, F9FirstInst, F3);
   F9AA.instructionCanReach(A, F9FirstInst, F4);
 
@@ -194,8 +194,8 @@ TEST_F(AttributorTestBase, AAReachabilityTest) {
   ASSERT_TRUE(F1AA.canReach(A, F3));
   ASSERT_FALSE(F1AA.canReach(A, F4));
 
-  ASSERT_TRUE(F7AA.instructionCanReach(A, F7FirstCB, F3));
-  ASSERT_TRUE(F7AA.instructionCanReach(A, F7FirstCB, F4));
+  ASSERT_TRUE(F7AA.canReach(A, F7FirstCB, F3));
+  ASSERT_FALSE(F7AA.canReach(A, F7FirstCB, F4));
 
   // Assumed to be reacahable, since F6 can reach a function with
   // a unknown callee.


        


More information about the llvm-commits mailing list