From fc21f2d7bae2e0be630470cc7ca9323ed5859892 Mon Sep 17 00:00:00 2001 From: Johannes Doerfert Date: Mon, 12 Dec 2022 15:17:27 -0800 Subject: [PATCH] [Attributor] Introduce AA[Intra/Inter]Reachability We had two AAs for reachability but it was very cumbersome to extend them. We also had some fallback to use LLVM-core mechanisms and cache the result. The new design shares the query code and interface nicely between AAIntraFnReachability and AAInterFnReachability. As part of the rewrite we also added the ExclusionSet to the queries. --- llvm/include/llvm/Transforms/IPO/Attributor.h | 153 ++-- llvm/lib/Transforms/IPO/Attributor.cpp | 100 ++- .../Transforms/IPO/AttributorAttributes.cpp | 601 +++++++------ .../Attributor/ArgumentPromotion/alloca-as.ll | 2 +- .../Attributor/ArgumentPromotion/dbg.ll | 2 +- .../ArgumentPromotion/naked_functions.ll | 2 +- .../nonzero-address-spaces.ll | 2 +- .../Attributor/ArgumentPromotion/pr27568.ll | 2 +- .../Attributor/ArgumentPromotion/profile.ll | 2 +- .../Attributor/ArgumentPromotion/variadic.ll | 2 +- .../Attributor/IPConstantProp/naked-return.ll | 2 +- .../IPConstantProp/return-argument.ll | 2 +- llvm/test/Transforms/Attributor/cgscc_bugs.ll | 2 +- llvm/test/Transforms/Attributor/depgraph.ll | 8 +- .../Transforms/Attributor/internal-noalias.ll | 27 +- .../Transforms/Attributor/liveness_chains.ll | 2 +- llvm/test/Transforms/Attributor/lowerheap.ll | 2 +- llvm/test/Transforms/Attributor/misc.ll | 2 +- llvm/test/Transforms/Attributor/noalias.ll | 2 +- llvm/test/Transforms/Attributor/noundef.ll | 2 +- .../Attributor/value-simplify-assume.ll | 28 +- .../Attributor/value-simplify-pointer-info.ll | 6 +- .../Attributor/value-simplify-reachability.ll | 817 ++++++++++++++++++ .../Transforms/IPO/AttributorTest.cpp | 24 +- 24 files changed, 1339 insertions(+), 455 deletions(-) create mode 100644 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 768e5ea2ec0e6..3d622fd434b9a 100644 --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -151,6 +151,7 @@ class Function; /// Abstract Attribute helper functions. namespace AA { +using InstExclusionSetTy = SmallPtrSet; /// Flags to distinguish intra-procedural queries from *potentially* /// inter-procedural queries. Not that information can be valid for both and @@ -352,23 +353,26 @@ 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. 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 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. bool isPotentiallyReachable( Attributor &A, const Instruction &FromI, const Instruction &ToI, const AbstractAttribute &QueryingAA, + const AA::InstExclusionSetTy *ExclusionSet = nullptr, std::function 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, - std::function GoBackwardsCB); + const AA::InstExclusionSetTy *ExclusionSet = nullptr, + std::function GoBackwardsCB = nullptr); } // namespace AA @@ -410,6 +414,39 @@ struct DenseMapInfo : public DenseMapInfo { } }; +template <> +struct DenseMapInfo + : public DenseMapInfo { + using super = DenseMapInfo; + static inline const AA::InstExclusionSetTy *getEmptyKey() { + return static_cast(super::getEmptyKey()); + } + static inline const AA::InstExclusionSetTy *getTombstoneKey() { + return static_cast( + super::getTombstoneKey()); + } + static unsigned getHashValue(const AA::InstExclusionSetTy *BES) { + unsigned H = 0; + if (BES) + for (const auto *II : *BES) + H += DenseMapInfo::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; @@ -1222,21 +1259,16 @@ struct InformationCache { /// Return the map conaining all the knowledge we have from `llvm.assume`s. const RetainedKnowledgeMap &getKnowledgeMap() const { return KnowledgeMap; } - /// 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(F), - AG.getAnalysis(F)); - PotentiallyReachableMap.insert(std::make_pair(KeyPair, Result)); - return Result; + /// 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; } /// Check whether \p F is part of module slice. @@ -1305,16 +1337,15 @@ struct InformationCache { /// A container for all instructions that are only used by `llvm.assume`. SetVector AssumeOnlyValues; + /// Cache for block sets to allow reuse. + DenseSet BESets; + /// Getters for analysis. AnalysisGetter &AG; /// Set of inlineable functions SmallPtrSet InlineableFunctions; - /// A map for caching results of queries for isPotentiallyReachable - DenseMap, bool> - PotentiallyReachableMap; - /// The triple describing the target machine. Triple TargetTriple; @@ -3392,42 +3423,30 @@ struct AAUndefinedBehavior }; /// An abstract interface to determine reachability of point A to B. -struct AAReachability : public StateWrapper { +struct AAIntraFnReachability + : public StateWrapper { using Base = StateWrapper; - AAReachability(const IRPosition &IRP, Attributor &A) : Base(IRP) {} + AAIntraFnReachability(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. - 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); - } + virtual bool isAssumedReachable( + Attributor &A, const Instruction &From, const Instruction &To, + const AA::InstExclusionSetTy *ExclusionSet = nullptr) const = 0; /// Create an abstract attribute view for the position \p IRP. - static AAReachability &createForPosition(const IRPosition &IRP, - Attributor &A); + static AAIntraFnReachability &createForPosition(const IRPosition &IRP, + Attributor &A); /// See AbstractAttribute::getName() - const std::string getName() const override { return "AAReachability"; } + const std::string getName() const override { return "AAIntraFnReachability"; } /// See AbstractAttribute::getIdAddr() const char *getIdAddr() const override { return &ID; } /// This function should return true if the type of the \p AA is - /// AAReachability + /// AAIntraFnReachability static bool classof(const AbstractAttribute *AA) { return (AA->getIdAddr() == &ID); } @@ -4948,35 +4967,33 @@ struct AAExecutionDomain }; /// An abstract Attribute for computing reachability between functions. -struct AAFunctionReachability +struct AAInterFnReachability : public StateWrapper { using Base = StateWrapper; - AAFunctionReachability(const IRPosition &IRP, Attributor &A) : Base(IRP) {} - - /// See AbstractAttribute::isQueryAA. - bool isQueryAA() const override { return true; } + AAInterFnReachability(const IRPosition &IRP, Attributor &A) : Base(IRP) {} /// If the function represented by this possition can reach \p 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; + bool canReach(Attributor &A, const Function &Fn) const { + Function *Scope = getAnchorScope(); + if (!Scope || Scope->isDeclaration()) + return true; + return instructionCanReach(A, Scope->getEntryBlock().front(), Fn); + } /// Can \p Inst reach \p Fn. /// See also AA::isPotentiallyReachable. - virtual bool instructionCanReach(Attributor &A, const Instruction &Inst, - const Function &Fn) const = 0; + virtual bool instructionCanReach( + Attributor &A, const Instruction &Inst, const Function &Fn, + const AA::InstExclusionSetTy *ExclusionSet = nullptr, + SmallPtrSet *Visited = nullptr) const = 0; /// Create an abstract attribute view for the position \p IRP. - static AAFunctionReachability &createForPosition(const IRPosition &IRP, - Attributor &A); + static AAInterFnReachability &createForPosition(const IRPosition &IRP, + Attributor &A); /// See AbstractAttribute::getName() - const std::string getName() const override { - return "AAFunctionReachability"; - } + const std::string getName() const override { return "AAInterFnReachability"; } /// See AbstractAttribute::getIdAddr() const char *getIdAddr() const override { return &ID; } @@ -4988,10 +5005,6 @@ struct AAFunctionReachability /// 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 f4f9c54b9355a..e3dcfd95a68ee 100644 --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -570,19 +570,27 @@ static bool isPotentiallyReachable(Attributor &A, const Instruction &FromI, const Instruction *ToI, const Function &ToFn, const AbstractAttribute &QueryingAA, + const AA::InstExclusionSetTy *ExclusionSet, std::function GoBackwardsCB) { - 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] 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] check @" << ToFn.getName() << " from " << FromI - << " is not checked backwards, abort\n"); + << " is not checked backwards and does not have an " + "exclusion set, abort\n"); return true; } @@ -601,9 +609,10 @@ isPotentiallyReachable(Attributor &A, const Instruction &FromI, return true; LLVM_DEBUG(dbgs() << "[AA] check " << *ToI << " from " << *CurFromI << " intraprocedurally\n"); - const auto &ReachabilityAA = A.getAAFor( + const auto &ReachabilityAA = A.getAAFor( QueryingAA, IRPosition::function(ToFn), DepClassTy::OPTIONAL); - bool Result = ReachabilityAA.isAssumedReachable(A, *CurFromI, *ToI); + bool Result = + ReachabilityAA.isAssumedReachable(A, *CurFromI, *ToI, ExclusionSet); LLVM_DEBUG(dbgs() << "[AA] " << *CurFromI << " " << (Result ? "can potentially " : "cannot ") << "reach " << *ToI << " [Intra]\n"); @@ -611,15 +620,57 @@ isPotentiallyReachable(Attributor &A, const Instruction &FromI, return true; } - // Check if the current instruction is already known to reach the ToFn. - const auto &FnReachabilityAA = A.getAAFor( + bool Result = true; + if (!ToFn.isDeclaration() && ToI) { + const auto &ToReachabilityAA = A.getAAFor( + 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( + 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( QueryingAA, IRPosition::function(*FromFn), DepClassTy::OPTIONAL); - 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) + 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"); 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. @@ -642,7 +693,6 @@ isPotentiallyReachable(Attributor &A, const Instruction &FromI, return true; }; - bool UsedAssumedInformation = false; Result = !A.checkForAllCallSites(CheckCallSite, *FromFn, /* RequireAllCallSites */ true, &QueryingAA, UsedAssumedInformation); @@ -663,20 +713,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 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, - GoBackwardsCB); + ExclusionSet, GoBackwardsCB); } bool AA::isPotentiallyReachable( Attributor &A, const Instruction &FromI, const Function &ToFn, const AbstractAttribute &QueryingAA, + const AA::InstExclusionSetTy *ExclusionSet, std::function GoBackwardsCB) { return ::isPotentiallyReachable(A, FromI, /* ToI */ nullptr, ToFn, QueryingAA, - GoBackwardsCB); + ExclusionSet, 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 482aec1cb5b31..7791a39616e32 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(AAReachability) +PIPE_OPERATOR(AAIntraFnReachability) 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(AAFunctionReachability) +PIPE_OPERATOR(AAInterFnReachability) PIPE_OPERATOR(AAPointerInfo) PIPE_OPERATOR(AAAssumptionInfo) @@ -1160,7 +1160,16 @@ 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(I) && Acc.isWriteOrAssumption())) + ExclusionSet.insert(Acc.getRemoteInst()); + } + if ((!FindInterferingWrites || !Acc.isWriteOrAssumption()) && (!FindInterferingReads || !Acc.isRead())) return true; @@ -1188,11 +1197,11 @@ struct AAPointerInfoImpl // hide the effect of this one. auto CanSkipAccess = [&](const Access &Acc, bool Exact) { if ((!Acc.isWriteOrAssumption() || - !AA::isPotentiallyReachable(A, *Acc.getLocalInst(), I, QueryingAA, - IsLiveInCalleeCB)) && + !AA::isPotentiallyReachable(A, *Acc.getRemoteInst(), I, QueryingAA, + &ExclusionSet, IsLiveInCalleeCB)) && (!Acc.isRead() || - !AA::isPotentiallyReachable(A, I, *Acc.getLocalInst(), QueryingAA, - IsLiveInCalleeCB))) + !AA::isPotentiallyReachable(A, I, *Acc.getRemoteInst(), QueryingAA, + &ExclusionSet, IsLiveInCalleeCB))) return true; if (!DT || !UseDominanceReasoning) @@ -2749,9 +2758,9 @@ struct AANoRecurseFunction final : AANoRecurseImpl { return ChangeStatus::UNCHANGED; } - const AAFunctionReachability &EdgeReachability = - A.getAAFor(*this, getIRPosition(), - DepClassTy::REQUIRED); + const AAInterFnReachability &EdgeReachability = + A.getAAFor(*this, getIRPosition(), + DepClassTy::REQUIRED); if (EdgeReachability.canReach(A, *getAnchorScope())) return indicatePessimisticFixpoint(); return ChangeStatus::UNCHANGED; @@ -3264,30 +3273,245 @@ struct AAWillReturnCallSite final : AAWillReturnImpl { }; } // namespace -/// -------------------AAReachability Attribute-------------------------- +/// -------------------AAIntraFnReachability Attribute-------------------------- + +/// All information associated with a reachability query. This boilerplate code +/// is used by both AAIntraFnReachability and AAInterFnReachability, with +/// different \p ToTy values. +template 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 struct DenseMapInfo *> { + using InstSetDMI = DenseMapInfo; + using PairDMI = DenseMapInfo>; + + static ReachabilityQueryInfo EmptyKey; + static ReachabilityQueryInfo TombstoneKey; + + static inline ReachabilityQueryInfo *getEmptyKey() { return &EmptyKey; } + static inline ReachabilityQueryInfo *getTombstoneKey() { + return &TombstoneKey; + } + static unsigned getHashValue(const ReachabilityQueryInfo *RQI) { + unsigned H = PairDMI ::getHashValue({RQI->From, RQI->To}); + H += InstSetDMI::getHashValue(RQI->ExclusionSet); + return H; + } + static bool isEqual(const ReachabilityQueryInfo *LHS, + const ReachabilityQueryInfo *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 \ + DenseMapInfo *>::EmptyKey = \ + ReachabilityQueryInfo( \ + DenseMapInfo::getEmptyKey(), \ + DenseMapInfo::getEmptyKey()); \ + template <> \ + ReachabilityQueryInfo \ + DenseMapInfo *>::TombstoneKey = \ + ReachabilityQueryInfo( \ + DenseMapInfo::getTombstoneKey(), \ + DenseMapInfo::getTombstoneKey()); + +DefineKeys(Instruction) DefineKeys(Function) +#undef DefineKeys + +} // namespace llvm namespace { -struct AAReachabilityImpl : AAReachability { - AAReachabilityImpl(const IRPosition &IRP, Attributor &A) - : AAReachability(IRP, A) {} + +template +struct CachedReachabilityAA : public BaseTy { + using RQITy = ReachabilityQueryInfo; + + CachedReachabilityAA(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; + } const std::string getAsStr() const override { // TODO: Return the number of reachable queries. - return "reachable"; + return "#queries(" + std::to_string(QueryVector.size()) + ")"; } - /// See AbstractAttribute::updateImpl(...). - ChangeStatus updateImpl(Attributor &A) override { - return ChangeStatus::UNCHANGED; + 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; } + +private: + bool InUpdate = false; + SmallVector QueryVector; + DenseSet QueryCache; }; -struct AAReachabilityFunction final : public AAReachabilityImpl { - AAReachabilityFunction(const IRPosition &IRP, Attributor &A) - : AAReachabilityImpl(IRP, A) {} +struct AAIntraFnReachabilityFunction final + : public CachedReachabilityAA { + AAIntraFnReachabilityFunction(const IRPosition &IRP, Attributor &A) + : CachedReachabilityAA(IRP, A) {} + + bool isAssumedReachable( + Attributor &A, const Instruction &From, const Instruction &To, + const AA::InstExclusionSetTy *ExclusionSet) const override { + auto *NonConstThis = const_cast(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 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 Visited; + SmallVector Worklist; + Worklist.push_back(FromBB); + + auto &LivenessAA = + A.getAAFor(*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); + } /// See AbstractAttribute::trackStatistics() - void trackStatistics() const override { STATS_DECLTRACK_FN_ATTR(reachable); } + void trackStatistics() const override {} }; } // namespace @@ -3530,7 +3754,7 @@ struct AANoAliasCallSiteArgument final : AANoAliasImpl { } if (!AA::isPotentiallyReachable( - A, *UserI, *getCtxI(), *this, + A, *UserI, *getCtxI(), *this, /* ExclusionSet */ nullptr, [ScopeFn](const Function &Fn) { return &Fn != ScopeFn; })) return true; } @@ -5159,7 +5383,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, + A, *CB, *Scope, *this, /* ExclusionSet */ nullptr, [Scope](const Function &Fn) { return &Fn != Scope; })) return false; return true; @@ -9874,289 +10098,98 @@ struct AACallEdgesFunction : public AACallEdgesImpl { } }; -struct AAFunctionReachabilityFunction : public AAFunctionReachability { -private: - struct QuerySet { - void markReachable(const Function &Fn) { - Reachable.insert(&Fn); - Unreachable.erase(&Fn); - } +/// -------------------AAInterFnReachability Attribute-------------------------- - /// If there is no information about the function std::nullopt is returned. - std::optional isCachedReachable(const Function &Fn) { - // Assume that we can reach the function. - // TODO: Be more specific with the unknown callee. - if (CanReachUnknownCallee) - return true; +struct AAInterFnReachabilityFunction + : public CachedReachabilityAA { + AAInterFnReachabilityFunction(const IRPosition &IRP, Attributor &A) + : CachedReachabilityAA(IRP, A) {} - if (Reachable.count(&Fn)) - return true; + bool instructionCanReach( + Attributor &A, const Instruction &From, const Function &To, + const AA::InstExclusionSetTy *ExclusionSet, + SmallPtrSet *Visited) const override { + assert(From.getFunction() == getAnchorScope() && "Queried the wrong AA!"); + auto *NonConstThis = const_cast(this); - if (Unreachable.count(&Fn)) - return false; - - return std::nullopt; - } - - /// Set of functions that we know for sure is reachable. - DenseSet Reachable; - - /// Set of functions that are unreachable, but might become reachable. - DenseSet Unreachable; - - /// 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 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 AAEdgesList, - const Function &Fn) { - std::optional 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 AAEdgesList, - const Function &Fn) const { - - // Handle the most trivial case first. - for (const auto *AAEdges : AAEdgesList) { - const SetVector &Edges = AAEdges->getOptimisticEdges(); - - if (Edges.count(const_cast(&Fn))) - return true; - } - - SmallVector Deps; - for (const auto &AAEdges : AAEdgesList) { - const SetVector &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; + 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; + } - // We don't need a dependency if the result is reachable. - const AAFunctionReachability &EdgeReachability = - A.getAAFor( - AA, IRPosition::function(*Edge), DepClassTy::NONE); - Deps.push_back(&EdgeReachability); + bool isReachableImpl(Attributor &A, RQITy &RQI) override { + return isReachableImpl(A, RQI, nullptr); + } - if (EdgeReachability.canReach(A, Fn)) - return true; - } - } + bool isReachableImpl(Attributor &A, RQITy &RQI, + SmallPtrSet *Visited) { - // The result is false for now, set dependencies and leave. - for (const auto *Dep : Deps) - A.recordDependence(*Dep, AA, DepClassTy::REQUIRED); + SmallPtrSet LocalVisited; + if (!Visited) + Visited = &LocalVisited; - return false; - } - }; + const auto &IntraFnReachability = A.getAAFor( + *this, IRPosition::function(*RQI.From->getFunction()), + DepClassTy::OPTIONAL); - /// Get call edges that can be reached by this instruction. - bool getReachableCallEdges(Attributor &A, const AAReachability &Reachability, - const Instruction &Inst, - SmallVector &Result) const { // Determine call like instructions that we can reach from the inst. + SmallVector ReachableCallBases; auto CheckCallBase = [&](Instruction &CBInst) { - if (!Reachability.isAssumedReachable(A, Inst, CBInst)) - return true; - - auto &CB = cast(CBInst); - const AACallEdges &AAEdges = A.getAAFor( - *this, IRPosition::callsite_function(CB), DepClassTy::REQUIRED); - - Result.push_back(&AAEdges); + if (IntraFnReachability.isAssumedReachable(A, *RQI.From, CBInst, + RQI.ExclusionSet)) + ReachableCallBases.push_back(cast(&CBInst)); return true; }; bool UsedAssumedInformation = false; - return A.checkForAllCallLikeInstructions(CheckCallBase, *this, - UsedAssumedInformation, - /* CheckBBLivenessOnly */ true); - } - -public: - AAFunctionReachabilityFunction(const IRPosition &IRP, Attributor &A) - : AAFunctionReachability(IRP, A) {} - - bool canReach(Attributor &A, const Function &Fn) const override { - if (!isValidState()) - return true; - - const AACallEdges &AAEdges = - A.getAAFor(*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(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( - *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(this); - QueryResolver &CBQuery = NonConstThis->CBQueries[&CB]; - - bool Result = CBQuery.isReachable(A, *NonConstThis, {&AAEdges}, Fn); - - return Result; - } - - bool instructionCanReach(Attributor &A, const Instruction &Inst, - const Function &Fn) const override { - if (!isValidState()) - return true; - - const auto &Reachability = A.getAAFor( - *this, IRPosition::function(*getAssociatedFunction()), - DepClassTy::REQUIRED); - - SmallVector 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(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); - } - - /// See AbstractAttribute::updateImpl(...). - ChangeStatus updateImpl(Attributor &A) override { - const AACallEdges &AAEdges = - A.getAAFor(*this, getIRPosition(), DepClassTy::REQUIRED); - ChangeStatus Change = ChangeStatus::UNCHANGED; - - Change |= WholeFunction.update(A, *this, {&AAEdges}); + if (!A.checkForAllCallLikeInstructions(CheckCallBase, *this, + UsedAssumedInformation, + /* CheckBBLivenessOnly */ true)) + return rememberResult(A, RQITy::Reachable::Yes, RQI); - for (auto &CBPair : CBQueries) { - const AACallEdges &AAEdges = A.getAAFor( - *this, IRPosition::callsite_function(*CBPair.first), - DepClassTy::REQUIRED); + for (CallBase *CB : ReachableCallBases) { + auto &CBEdges = A.getAAFor( + *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); - Change |= CBPair.second.update(A, *this, {&AAEdges}); - } + 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); + } - // Update the Instruction queries. - if (!InstQueries.empty()) { - const AAReachability *Reachability = &A.getAAFor( - *this, IRPosition::function(*getAssociatedFunction()), - DepClassTy::REQUIRED); + const AAInterFnReachability *InterFnReachability = this; + if (Fn != getAnchorScope()) + InterFnReachability = &A.getAAFor( + *this, IRPosition::function(*Fn), DepClassTy::OPTIONAL); - // Check for local callbases first. - for (auto &InstPair : InstQueries) { - SmallVector 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); + const Instruction &FnFirstInst = Fn->getEntryBlock().front(); + if (InterFnReachability->instructionCanReach(A, FnFirstInst, *RQI.To, + RQI.ExclusionSet, Visited)) + return rememberResult(A, RQITy::Reachable::Yes, 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))) + - "]"; + return rememberResult(A, RQITy::Reachable::No, RQI); } void trackStatistics() const override {} private: - 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 CBQueries; - - /// This is for instruction queries than scan "forward". - MapVector InstQueries; + SmallVector QueryVector; + DenseSet QueryCache; }; } // namespace @@ -11158,7 +11191,7 @@ const char AANoRecurse::ID = 0; const char AAWillReturn::ID = 0; const char AAUndefinedBehavior::ID = 0; const char AANoAlias::ID = 0; -const char AAReachability::ID = 0; +const char AAIntraFnReachability::ID = 0; const char AANoReturn::ID = 0; const char AAIsDead::ID = 0; const char AADereferenceable::ID = 0; @@ -11175,7 +11208,7 @@ const char AAPotentialConstantValues::ID = 0; const char AAPotentialValues::ID = 0; const char AANoUndef::ID = 0; const char AACallEdges::ID = 0; -const char AAFunctionReachability::ID = 0; +const char AAInterFnReachability::ID = 0; const char AAPointerInfo::ID = 0; const char AAAssumptionInfo::ID = 0; @@ -11300,9 +11333,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(AAFunctionReachability) +CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAIntraFnReachability) +CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAInterFnReachability) 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 53719c2b1e7af..afb7358473691 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=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=1 -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 77e8a214b81b8..d691d79dac264 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=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=1 -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 c10674f095381..257ad473afc7d 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=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=1 -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 3f56bc0b94821..4565b9cc33e26 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=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=1 -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 15dcfc9f20f14..32b9f8714803b 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=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=1 -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 13cc1794f9ab8..f1b79c87af934 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=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=1 -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 e9a09a6a2f1dc..e017d85181233 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=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=1 -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 3e23a32d313fd..1c9acece2c2a0 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=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=1 -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 3536ee0849bfb..0347cc3aba8ae 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=11 -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=5 -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 22332b91c0c50..8cf8280a4dc8a 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=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=6 -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 0531916d8c8a4..901e95f9de2f4 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@-1]} with state may-recurse ; GRAPH-EMPTY: -; GRAPH-NEXT: [AAFunctionReachability] for CtxI ' %2 = load i32, i32* %0, align 4' at position {fn:checkAndAdvance [checkAndAdvance@-1]} with state FunctionReachability [1,1] +; GRAPH-NEXT: [AAInterFnReachability] for CtxI ' %2 = load i32, i32* %0, align 4' at position {fn:checkAndAdvance [checkAndAdvance@-1]} with state #queries(1) ; GRAPH-EMPTY: -; GRAPH-NEXT: [AACallEdges] for CtxI ' %2 = load i32, i32* %0, align 4' at position {fn:checkAndAdvance [checkAndAdvance@-1]} with state CallEdges[0,1] +; GRAPH-NEXT: [AAIntraFnReachability] for CtxI ' %2 = load i32, i32* %0, align 4' at position {fn:checkAndAdvance [checkAndAdvance@-1]} with state #queries(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="{[AAFunctionReachability] -; DOT-DAG: Node[[Node30:0x[a-z0-9]+]] [shape=record,label="{[AACallEdges] +; 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[[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 74608c93b3bb3..2355660e1ee18 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]]) #[[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: [[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: [[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]]) #[[ATTR3]] +; 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: [[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]]) #[[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: [[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: [[ADD:%.*]] = add nsw i32 [[CALL1]], [[CALL2]] ; TUNIT-NEXT: ret i32 [[ADD]] ; @@ -158,11 +158,10 @@ 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: readwrite) uwtable +; TUNIT: Function Attrs: nofree noinline norecurse nosync nounwind willreturn memory(argmem: write) uwtable ; TUNIT-LABEL: define {{[^@]+}}@noalias_args_argmem_rn -; 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]] +; TUNIT-SAME: (i32* noalias nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[B:%.*]]) #[[ATTR3:[0-9]+]] { +; TUNIT-NEXT: ret i32 undef ; ; CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind willreturn memory(argmem: readwrite) uwtable ; CGSCC-LABEL: define {{[^@]+}}@noalias_args_argmem_rn @@ -181,9 +180,8 @@ define i32 @visible_local_3() { ; TUNIT-LABEL: define {{[^@]+}}@visible_local_3 ; TUNIT-SAME: () #[[ATTR2]] { ; TUNIT-NEXT: [[B:%.*]] = alloca i32, align 4 -; 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]] +; 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 ; ; CGSCC: Function Attrs: nofree nosync nounwind willreturn memory(none) ; CGSCC-LABEL: define {{[^@]+}}@visible_local_3 @@ -205,8 +203,9 @@ 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 nosync nounwind } -; TUNIT: attributes #[[ATTR4]] = { nofree nosync nounwind willreturn } +; 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 } ;. ; 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 7e45148343d0a..bca6434f43267 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=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=1 -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 0b10c1db35a9c..d725df78f3016 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=2 -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=1 -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 44e7f41abb58a..5a5165e9d144c 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=3 -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 ; ; 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 dcb93fe46cc33..571038e5d76ce 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* nocapture nofree writeonly align 4 [[P]]) #[[ATTR10]] +; TUNIT-NEXT: tail call void @only_store(i32* noalias 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 9ff18cf716e8f..19a50a362acd3 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=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=1 -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 aed444171f6d1..b9f6f27296e5a 100644 --- a/llvm/test/Transforms/Attributor/value-simplify-assume.ll +++ b/llvm/test/Transforms/Attributor/value-simplify-assume.ll @@ -347,7 +347,6 @@ 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 @@ -364,7 +363,6 @@ 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 @@ -424,9 +422,6 @@ 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 @@ -477,7 +472,6 @@ 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 @@ -530,9 +524,6 @@ 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 @@ -601,9 +592,7 @@ 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: 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: call void @llvm.assume(i1 noundef true) #[[ATTR6]] ; TUNIT-NEXT: br i1 [[COND]], label [[T:%.*]], label [[F:%.*]] ; TUNIT: t: ; TUNIT-NEXT: store i1 true, i1* [[STACK]], align 1 @@ -973,7 +962,6 @@ 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 @@ -990,7 +978,6 @@ 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 @@ -1050,9 +1037,6 @@ 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 @@ -1103,7 +1087,6 @@ 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 @@ -1156,9 +1139,6 @@ 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 @@ -1227,9 +1207,7 @@ 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: 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: call void @llvm.assume(i1 noundef true) #[[ATTR6]] ; TUNIT-NEXT: br i1 [[COND]], label [[T:%.*]], label [[F:%.*]] ; TUNIT: t: ; TUNIT-NEXT: store i1 true, i1* [[STACK]], align 1 @@ -1303,7 +1281,6 @@ 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]] @@ -1316,7 +1293,6 @@ 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 97a94cf2cd97b..b8ac6e4c9fe2a 100644 --- a/llvm/test/Transforms/Attributor/value-simplify-pointer-info.ll +++ b/llvm/test/Transforms/Attributor/value-simplify-pointer-info.ll @@ -542,13 +542,9 @@ 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: store i32 2, i32* [[A]], align 4 -; CHECK-NEXT: [[L:%.*]] = load i32, i32* [[A]], align 4 -; CHECK-NEXT: ret i32 [[L]] +; CHECK-NEXT: ret i32 2 ; %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 new file mode 100644 index 0000000000000..e1353511705d1 --- /dev/null +++ b/llvm/test/Transforms/Attributor/value-simplify-reachability.ll @@ -0,0 +1,817 @@ +; 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 + +@GInt1 = internal global i32 undef, align 4 +@GInt2 = internal global i32 zeroinitializer, align 4 +@GInt3 = internal global i32 undef, align 4 +@GInt4 = internal global i32 zeroinitializer, align 4 +@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 cd289a1a2a353..4a38684b0dbf7 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 AAFunctionReachability &F1AA = - A.getOrCreateAAFor(IRPosition::function(F1)); + const AAInterFnReachability &F1AA = + A.getOrCreateAAFor(IRPosition::function(F1)); - const AAFunctionReachability &F6AA = - A.getOrCreateAAFor(IRPosition::function(F6)); + const AAInterFnReachability &F6AA = + A.getOrCreateAAFor(IRPosition::function(F6)); - const AAFunctionReachability &F7AA = - A.getOrCreateAAFor(IRPosition::function(F7)); + const AAInterFnReachability &F7AA = + A.getOrCreateAAFor(IRPosition::function(F7)); - const AAFunctionReachability &F9AA = - A.getOrCreateAAFor(IRPosition::function(F9)); + const AAInterFnReachability &F9AA = + A.getOrCreateAAFor(IRPosition::function(F9)); F1AA.canReach(A, F3); F1AA.canReach(A, F4); F6AA.canReach(A, F4); - F7AA.canReach(A, F7FirstCB, F3); - F7AA.canReach(A, F7FirstCB, F4); + F7AA.instructionCanReach(A, F7FirstCB, F3); + F7AA.instructionCanReach(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.canReach(A, F7FirstCB, F3)); - ASSERT_FALSE(F7AA.canReach(A, F7FirstCB, F4)); + ASSERT_TRUE(F7AA.instructionCanReach(A, F7FirstCB, F3)); + ASSERT_TRUE(F7AA.instructionCanReach(A, F7FirstCB, F4)); // Assumed to be reacahable, since F6 can reach a function with // a unknown callee.