[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