From 31ad4dbcb91c419254670585aa51088aeeec79cf Mon Sep 17 00:00:00 2001 From: Johannes Doerfert Date: Sat, 17 Dec 2022 15:16:41 -0800 Subject: [PATCH] Reapply "[Attributor] Introduce AA[Intra/Inter]Reachability" This reverts commit e425a4c45618fcfa8ffb13be4ddfaa5d28aa38f1 after the memory leak has been fixed. --- llvm/include/llvm/Transforms/IPO/Attributor.h | 157 +++--- llvm/lib/Transforms/IPO/Attributor.cpp | 100 +++- llvm/lib/Transforms/IPO/AttributorAttributes.cpp | 601 +++++++++++---------- .../Attributor/ArgumentPromotion/alloca-as.ll | 2 +- .../Transforms/Attributor/ArgumentPromotion/dbg.ll | 2 +- .../ArgumentPromotion/naked_functions.ll | 2 +- .../ArgumentPromotion/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/openmp_parallel_for.ll | 21 +- .../Attributor/IPConstantProp/return-argument.ll | 2 +- llvm/test/Transforms/Attributor/cgscc_bugs.ll | 2 +- llvm/test/Transforms/Attributor/depgraph.ll | 8 +- .../test/Transforms/Attributor/internal-noalias.ll | 27 +- llvm/test/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 +- .../Transforms/Attributor/value-simplify-assume.ll | 28 +- .../Attributor/value-simplify-pointer-info.ll | 6 +- .../Attributor/value-simplify-reachability.ll | 57 +- llvm/unittests/Transforms/IPO/AttributorTest.cpp | 24 +- 25 files changed, 554 insertions(+), 505 deletions(-) diff --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h index 29231c6..dbf2892 100644 --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -152,6 +152,7 @@ class Function; /// Abstract Attribute helper functions. namespace AA { +using InstExclusionSetTy = SmallPtrSet; enum class GPUAddressSpace : unsigned { Generic = 0, @@ -350,23 +351,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); /// Return true if \p Obj is assumed to be a thread local object. bool isAssumedThreadLocalObject(Attributor &A, Value &Obj, @@ -412,6 +416,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; @@ -1132,6 +1169,10 @@ struct InformationCache { // the destructor manually. for (auto &It : FuncInfoMap) It.getSecond()->~FunctionInfo(); + // Same is true for the instruction exclusions sets. + using AA::InstExclusionSetTy; + for (auto *BES : BESets) + BES->~InstExclusionSetTy(); } /// Apply \p CB to all uses of \p F. If \p LookThroughConstantExprUses is @@ -1249,21 +1290,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. @@ -1332,16 +1368,15 @@ private: /// 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; @@ -3421,42 +3456,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); } @@ -4977,35 +5000,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; } @@ -5017,10 +5038,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 78f16fa..16e1293 100644 --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -572,19 +572,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; } @@ -603,9 +611,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"); @@ -613,15 +622,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. @@ -644,7 +695,6 @@ isPotentiallyReachable(Attributor &A, const Instruction &FromI, return true; }; - bool UsedAssumedInformation = false; Result = !A.checkForAllCallSites(CheckCallSite, *FromFn, /* RequireAllCallSites */ true, &QueryingAA, UsedAssumedInformation); @@ -665,20 +715,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); } bool AA::isAssumedThreadLocalObject(Attributor &A, Value &Obj, diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp index 6223f56..ba6afa4 100644 --- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp +++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp @@ -172,7 +172,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) @@ -182,7 +182,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) PIPE_OPERATOR(AAUnderlyingObjects) @@ -1127,7 +1127,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; @@ -1161,11 +1170,11 @@ struct AAPointerInfoImpl if (!IsSameThreadAsInst(Acc)) return false; 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) @@ -2765,9 +2774,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; @@ -3280,30 +3289,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 @@ -3546,7 +3770,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; } @@ -5175,7 +5399,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; @@ -9921,289 +10145,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; - - // 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 @@ -11358,7 +11391,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; @@ -11375,7 +11408,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; const char AAUnderlyingObjects::ID = 0; @@ -11502,9 +11535,9 @@ CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoFree) CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAUnderlyingObjects) 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 4b86722..b9469d1 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 1910894..f9a6e4c 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 b57e74b..ae82f46 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 27da1aa..3d7fc9f 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 925595c..071ffeb 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 0f818f2..b9f4317 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 d80d57f..9df41e4 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 3e23a32..1c9acec 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/openmp_parallel_for.ll b/llvm/test/Transforms/Attributor/IPConstantProp/openmp_parallel_for.ll index 8fe8175..bcfe365 100644 --- a/llvm/test/Transforms/Attributor/IPConstantProp/openmp_parallel_for.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/openmp_parallel_for.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=18 -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=16 -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 ; ; void bar(int, float, double); @@ -36,9 +36,7 @@ define dso_local void @foo(i32 %N) { ; TUNIT-NEXT: entry: ; TUNIT-NEXT: [[N_ADDR:%.*]] = alloca i32, align 4 ; TUNIT-NEXT: [[P:%.*]] = alloca float, align 4 -; TUNIT-NEXT: store i32 [[N]], ptr [[N_ADDR]], align 4 -; TUNIT-NEXT: store i32 7, ptr [[N_ADDR]], align 4 -; TUNIT-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr noundef nonnull align 8 dereferenceable(24) @[[GLOB1]], i32 noundef 3, ptr noundef @.omp_outlined., ptr noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[N_ADDR]], ptr noalias nocapture nofree nonnull readnone align 4 dereferenceable(4) undef, i64 undef) +; TUNIT-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr noundef nonnull align 8 dereferenceable(24) @[[GLOB1]], i32 noundef 3, ptr noundef @.omp_outlined., ptr noalias nocapture nofree nonnull readnone align 4 dereferenceable(4) undef, ptr noalias nocapture nofree nonnull readnone align 4 dereferenceable(4) undef, i64 undef) ; TUNIT-NEXT: ret void ; ; CGSCC-LABEL: define {{[^@]+}}@foo @@ -64,7 +62,7 @@ entry: define internal void @.omp_outlined.(ptr noalias %.global_tid., ptr noalias %.bound_tid., ptr dereferenceable(4) %N, ptr dereferenceable(4) %p, i64 %q) { ; TUNIT-LABEL: define {{[^@]+}}@.omp_outlined. -; TUNIT-SAME: (ptr noalias nocapture nofree readonly [[DOTGLOBAL_TID_:%.*]], ptr noalias nocapture nofree readnone [[DOTBOUND_TID_:%.*]], ptr noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[N:%.*]], ptr noalias nocapture nofree nonnull readnone align 4 dereferenceable(4) [[P:%.*]], i64 [[Q:%.*]]) { +; TUNIT-SAME: (ptr noalias nocapture nofree readonly [[DOTGLOBAL_TID_:%.*]], ptr noalias nocapture nofree readnone [[DOTBOUND_TID_:%.*]], ptr noalias nocapture nofree nonnull readnone align 4 dereferenceable(4) [[N:%.*]], ptr noalias nocapture nofree nonnull readnone align 4 dereferenceable(4) [[P:%.*]], i64 [[Q:%.*]]) { ; TUNIT-NEXT: entry: ; TUNIT-NEXT: [[Q_ADDR:%.*]] = alloca i64, align 8 ; TUNIT-NEXT: [[DOTOMP_LB:%.*]] = alloca i32, align 4 @@ -72,19 +70,16 @@ define internal void @.omp_outlined.(ptr noalias %.global_tid., ptr noalias %.bo ; TUNIT-NEXT: [[DOTOMP_STRIDE:%.*]] = alloca i32, align 4 ; TUNIT-NEXT: [[DOTOMP_IS_LAST:%.*]] = alloca i32, align 4 ; TUNIT-NEXT: store i64 4617315517961601024, ptr [[Q_ADDR]], align 8 -; TUNIT-NEXT: [[TMP:%.*]] = load i32, ptr [[N]], align 4 -; TUNIT-NEXT: [[SUB3:%.*]] = add nsw i32 [[TMP]], -3 -; TUNIT-NEXT: [[CMP:%.*]] = icmp sgt i32 [[TMP]], 2 -; TUNIT-NEXT: br i1 [[CMP]], label [[OMP_PRECOND_THEN:%.*]], label [[OMP_PRECOND_END:%.*]] +; TUNIT-NEXT: br label [[OMP_PRECOND_THEN:%.*]] ; TUNIT: omp.precond.then: ; TUNIT-NEXT: store i32 0, ptr [[DOTOMP_LB]], align 4 -; TUNIT-NEXT: store i32 [[SUB3]], ptr [[DOTOMP_UB]], align 4 +; TUNIT-NEXT: store i32 4, ptr [[DOTOMP_UB]], align 4 ; TUNIT-NEXT: store i32 1, ptr [[DOTOMP_STRIDE]], align 4 ; TUNIT-NEXT: store i32 0, ptr [[DOTOMP_IS_LAST]], align 4 ; TUNIT-NEXT: [[TMP5:%.*]] = load i32, ptr [[DOTGLOBAL_TID_]], align 4 ; TUNIT-NEXT: call void @__kmpc_for_static_init_4(ptr noundef nonnull align 8 dereferenceable(24) @[[GLOB0]], i32 [[TMP5]], i32 noundef 34, ptr noundef nonnull align 4 dereferenceable(4) [[DOTOMP_IS_LAST]], ptr noundef nonnull align 4 dereferenceable(4) [[DOTOMP_LB]], ptr noundef nonnull align 4 dereferenceable(4) [[DOTOMP_UB]], ptr noundef nonnull align 4 dereferenceable(4) [[DOTOMP_STRIDE]], i32 noundef 1, i32 noundef 1) ; TUNIT-NEXT: [[TMP6:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4 -; TUNIT-NEXT: [[CMP6:%.*]] = icmp sgt i32 [[TMP6]], [[SUB3]] +; TUNIT-NEXT: [[CMP6:%.*]] = icmp sgt i32 [[TMP6]], 4 ; TUNIT-NEXT: br i1 [[CMP6]], label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] ; TUNIT: cond.true: ; TUNIT-NEXT: br label [[COND_END:%.*]] @@ -92,7 +87,7 @@ define internal void @.omp_outlined.(ptr noalias %.global_tid., ptr noalias %.bo ; TUNIT-NEXT: [[TMP7:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4 ; TUNIT-NEXT: br label [[COND_END]] ; TUNIT: cond.end: -; TUNIT-NEXT: [[COND:%.*]] = phi i32 [ [[SUB3]], [[COND_TRUE]] ], [ [[TMP7]], [[COND_FALSE]] ] +; TUNIT-NEXT: [[COND:%.*]] = phi i32 [ 4, [[COND_TRUE]] ], [ [[TMP7]], [[COND_FALSE]] ] ; TUNIT-NEXT: store i32 [[COND]], ptr [[DOTOMP_UB]], align 4 ; TUNIT-NEXT: [[TMP8:%.*]] = load i32, ptr [[DOTOMP_LB]], align 4 ; TUNIT-NEXT: br label [[OMP_INNER_FOR_COND:%.*]] @@ -118,7 +113,7 @@ define internal void @.omp_outlined.(ptr noalias %.global_tid., ptr noalias %.bo ; TUNIT: omp.loop.exit: ; TUNIT-NEXT: [[TMP12:%.*]] = load i32, ptr [[DOTGLOBAL_TID_]], align 4 ; TUNIT-NEXT: call void @__kmpc_for_static_fini(ptr noundef nonnull align 8 dereferenceable(24) @[[GLOB0]], i32 [[TMP12]]) -; TUNIT-NEXT: br label [[OMP_PRECOND_END]] +; TUNIT-NEXT: br label [[OMP_PRECOND_END:%.*]] ; TUNIT: omp.precond.end: ; TUNIT-NEXT: ret void ; diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll b/llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll index 4c25fc4..c04fe58 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=13 -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 f905598..3ddab4a 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 fce4e24..ded4312 100644 --- a/llvm/test/Transforms/Attributor/depgraph.ll +++ b/llvm/test/Transforms/Attributor/depgraph.ll @@ -146,9 +146,9 @@ define ptr @checkAndAdvance(ptr align 16 %0) { ; GRAPH-EMPTY: ; GRAPH-NEXT: [AANoRecurse] for CtxI ' %2 = load i32, ptr %0, align 4' at position {fn:checkAndAdvance [checkAndAdvance@-1]} with state may-recurse ; GRAPH-EMPTY: -; GRAPH-NEXT: [AAFunctionReachability] for CtxI ' %2 = load i32, ptr %0, align 4' at position {fn:checkAndAdvance [checkAndAdvance@-1]} with state FunctionReachability [1,1] +; GRAPH-NEXT: [AAInterFnReachability] for CtxI ' %2 = load i32, ptr %0, align 4' at position {fn:checkAndAdvance [checkAndAdvance@-1]} with state #queries(1) ; GRAPH-EMPTY: -; GRAPH-NEXT: [AACallEdges] for CtxI ' %2 = load i32, ptr %0, align 4' at position {fn:checkAndAdvance [checkAndAdvance@-1]} with state CallEdges[0,1] +; GRAPH-NEXT: [AAIntraFnReachability] for CtxI ' %2 = load i32, ptr %0, align 4' at position {fn:checkAndAdvance [checkAndAdvance@-1]} with state #queries(1) ; GRAPH-EMPTY: ; GRAPH-NEXT: [AACallEdges] for CtxI ' %6 = call ptr @checkAndAdvance(ptr %5)' at position {cs: [@-1]} with state CallEdges[0,1] ; GRAPH-EMPTY: @@ -304,8 +304,8 @@ define ptr @checkAndAdvance(ptr 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 40ea982..29d3f27 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(ptr noalias %A, ptr noalias %B) #0 { ; TUNIT-LABEL: define {{[^@]+}}@visible ; TUNIT-SAME: (ptr noalias nocapture nofree readonly [[A:%.*]], ptr noalias nocapture nofree readonly align 4 [[B:%.*]]) #[[ATTR0:[0-9]+]] { ; TUNIT-NEXT: entry: -; TUNIT-NEXT: [[CALL1:%.*]] = call i32 @noalias_args(ptr noalias nocapture nofree readonly align 4 [[A]], ptr noalias nocapture nofree readonly align 4 [[B]]) #[[ATTR3:[0-9]+]] -; TUNIT-NEXT: [[CALL2:%.*]] = call i32 @noalias_args_argmem(ptr noalias nocapture nofree readonly align 4 [[A]], ptr noalias nocapture nofree readonly align 4 [[B]]) #[[ATTR3]] +; TUNIT-NEXT: [[CALL1:%.*]] = call i32 @noalias_args(ptr noalias nocapture nofree readonly align 4 [[A]], ptr noalias nocapture nofree readonly align 4 [[B]]) #[[ATTR4:[0-9]+]] +; TUNIT-NEXT: [[CALL2:%.*]] = call i32 @noalias_args_argmem(ptr noalias nocapture nofree readonly align 4 [[A]], ptr 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(ptr %A, ptr %B) #0 { ; TUNIT-NEXT: [[TMP0:%.*]] = load i32, ptr [[A]], align 4 ; TUNIT-NEXT: [[TMP1:%.*]] = load i32, ptr [[B]], align 4 ; TUNIT-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP0]], [[TMP1]] -; TUNIT-NEXT: [[CALL:%.*]] = call i32 @noalias_args_argmem(ptr nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[A]], ptr noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[B]]) #[[ATTR3]] +; TUNIT-NEXT: [[CALL:%.*]] = call i32 @noalias_args_argmem(ptr nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[A]], ptr 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(ptr %A) #0 { ; TUNIT-NEXT: entry: ; TUNIT-NEXT: [[B:%.*]] = alloca i32, align 4 ; TUNIT-NEXT: store i32 5, ptr [[B]], align 4 -; TUNIT-NEXT: [[CALL1:%.*]] = call i32 @noalias_args(ptr nocapture nofree readonly align 4 [[A]], ptr noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[B]]) #[[ATTR3]] -; TUNIT-NEXT: [[CALL2:%.*]] = call i32 @noalias_args_argmem(ptr nocapture nofree readonly align 4 [[A]], ptr noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[B]]) #[[ATTR3]] +; TUNIT-NEXT: [[CALL1:%.*]] = call i32 @noalias_args(ptr nocapture nofree readonly align 4 [[A]], ptr noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[B]]) #[[ATTR4]] +; TUNIT-NEXT: [[CALL2:%.*]] = call i32 @noalias_args_argmem(ptr nocapture nofree readonly align 4 [[A]], ptr 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(ptr %A, ptr %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: (ptr noalias nocapture nofree noundef nonnull align 4 dereferenceable(4) [[B:%.*]]) #[[ATTR1]] { -; TUNIT-NEXT: [[T0:%.*]] = load i32, ptr [[B]], align 4 -; TUNIT-NEXT: ret i32 [[T0]] +; TUNIT-SAME: (ptr 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, ptr [[B]], align 4 -; TUNIT-NEXT: [[CALL:%.*]] = call i32 @noalias_args_argmem_rn(ptr 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(ptr 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 7e45148..bca6434 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 6aee518..f98beac 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(ptr) #0 diff --git a/llvm/test/Transforms/Attributor/misc.ll b/llvm/test/Transforms/Attributor/misc.ll index 44e7f41..5a5165e 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 dcb93fe..571038e 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 ef9e1364..69d637f 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 6a2cbe6..3d42b20 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 noundef [[COND:%.*]]) #[[ATTR3]] { ; TUNIT-NEXT: [[STACK:%.*]] = alloca i1, align 1 -; TUNIT-NEXT: store i1 [[ARG]], ptr [[STACK]], align 1 ; TUNIT-NEXT: br i1 [[COND]], label [[T:%.*]], label [[F:%.*]] ; TUNIT: t: ; TUNIT-NEXT: store i1 true, ptr [[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 noundef [[COND:%.*]]) #[[ATTR3]] { ; CGSCC-NEXT: [[STACK:%.*]] = alloca i1, align 1 -; CGSCC-NEXT: store i1 [[ARG]], ptr [[STACK]], align 1 ; CGSCC-NEXT: br i1 [[COND]], label [[T:%.*]], label [[F:%.*]] ; CGSCC: t: ; CGSCC-NEXT: store i1 true, ptr [[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 noundef [[COND:%.*]]) #[[ATTR3]] { ; TUNIT-NEXT: [[STACK:%.*]] = alloca i1, align 1 -; TUNIT-NEXT: store i1 [[ARG]], ptr [[STACK]], align 1 -; TUNIT-NEXT: [[L:%.*]] = load i1, ptr [[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, ptr [[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 noundef [[COND:%.*]]) #[[ATTR3]] { ; TUNIT-NEXT: [[STACK:%.*]] = alloca i1, align 1 -; TUNIT-NEXT: store i1 [[ARG]], ptr [[STACK]], align 1 ; TUNIT-NEXT: br i1 [[COND]], label [[T:%.*]], label [[F:%.*]] ; TUNIT: t: ; TUNIT-NEXT: store i1 true, ptr [[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 noundef [[COND:%.*]]) #[[ATTR3]] { ; TUNIT-NEXT: [[STACK:%.*]] = alloca i1, align 1 -; TUNIT-NEXT: store i1 [[ARG]], ptr [[STACK]], align 1 -; TUNIT-NEXT: [[L1:%.*]] = load i1, ptr [[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, ptr [[STACK]], align 1 @@ -601,9 +592,7 @@ define i1 @assume_5c_nr(i1 %cond) norecurse { ; TUNIT-LABEL: define {{[^@]+}}@assume_5c_nr ; TUNIT-SAME: (i1 noundef [[COND:%.*]]) #[[ATTR3]] { ; TUNIT-NEXT: [[STACK:%.*]] = alloca i1, align 1 -; TUNIT-NEXT: store i1 true, ptr [[STACK]], align 1 -; TUNIT-NEXT: [[L1:%.*]] = load i1, ptr [[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, ptr [[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 noundef [[COND:%.*]]) #[[ATTR3]] { ; TUNIT-NEXT: [[STACK:%.*]] = alloca i1, align 1 -; TUNIT-NEXT: store i1 [[ARG]], ptr [[STACK]], align 1 ; TUNIT-NEXT: br i1 [[COND]], label [[T:%.*]], label [[F:%.*]] ; TUNIT: t: ; TUNIT-NEXT: store i1 true, ptr [[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 noundef [[COND:%.*]]) #[[ATTR3]] { ; CGSCC-NEXT: [[STACK:%.*]] = alloca i1, align 1 -; CGSCC-NEXT: store i1 [[ARG]], ptr [[STACK]], align 1 ; CGSCC-NEXT: br i1 [[COND]], label [[T:%.*]], label [[F:%.*]] ; CGSCC: t: ; CGSCC-NEXT: store i1 true, ptr [[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 noundef [[COND:%.*]]) #[[ATTR3]] { ; TUNIT-NEXT: [[STACK:%.*]] = alloca i1, align 1 -; TUNIT-NEXT: store i1 [[ARG]], ptr [[STACK]], align 1 -; TUNIT-NEXT: [[L:%.*]] = load i1, ptr [[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, ptr [[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 noundef [[COND:%.*]]) #[[ATTR3]] { ; TUNIT-NEXT: [[STACK:%.*]] = alloca i1, align 1 -; TUNIT-NEXT: store i1 [[ARG]], ptr [[STACK]], align 1 ; TUNIT-NEXT: br i1 [[COND]], label [[T:%.*]], label [[F:%.*]] ; TUNIT: t: ; TUNIT-NEXT: store i1 true, ptr [[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 noundef [[COND:%.*]]) #[[ATTR3]] { ; TUNIT-NEXT: [[STACK:%.*]] = alloca i1, align 1 -; TUNIT-NEXT: store i1 [[ARG]], ptr [[STACK]], align 1 -; TUNIT-NEXT: [[L1:%.*]] = load i1, ptr [[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, ptr [[STACK]], align 1 @@ -1227,9 +1207,7 @@ define i1 @assume_5c(i1 %cond) { ; TUNIT-LABEL: define {{[^@]+}}@assume_5c ; TUNIT-SAME: (i1 noundef [[COND:%.*]]) #[[ATTR3]] { ; TUNIT-NEXT: [[STACK:%.*]] = alloca i1, align 1 -; TUNIT-NEXT: store i1 true, ptr [[STACK]], align 1 -; TUNIT-NEXT: [[L1:%.*]] = load i1, ptr [[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, ptr [[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, ptr @Gstatic_int1, align 4 -; TUNIT-NEXT: store i32 13, ptr @Gstatic_int1, align 4 ; TUNIT-NEXT: store i32 17, ptr @Gstatic_int1, align 4 ; TUNIT-NEXT: [[LGS3:%.*]] = load i32, ptr @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, ptr @Gstatic_int1, align 4 -; CGSCC-NEXT: store i32 13, ptr @Gstatic_int1, align 4 ; CGSCC-NEXT: store i32 17, ptr @Gstatic_int1, align 4 ; CGSCC-NEXT: [[LGS3:%.*]] = load i32, ptr @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 b6b06b0..7570b4c 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 index 4ed604b..419a636 100644 --- a/llvm/test/Transforms/Attributor/value-simplify-reachability.ll +++ b/llvm/test/Transforms/Attributor/value-simplify-reachability.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=7 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT +; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=11 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC @GInt1 = internal global i32 undef, align 4 @@ -175,11 +175,9 @@ 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: [[L0:%.*]] = load i32, ptr @GInt3, align 4 -; TUNIT-NEXT: call void @useI32(i32 [[L0]]) +; TUNIT-NEXT: call void @useI32(i32 1) ; TUNIT-NEXT: store i32 1, ptr @GInt3, align 4 -; TUNIT-NEXT: [[L1:%.*]] = load i32, ptr @GInt3, align 4 -; TUNIT-NEXT: call void @useI32(i32 [[L1]]) +; 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 @@ -190,18 +188,15 @@ define void @entry3(i1 %c, i32 %v) { ; 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: [[L4:%.*]] = load i32, ptr @GInt3, align 4 -; TUNIT-NEXT: call void @useI32(i32 [[L4]]) +; 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: [[L0:%.*]] = load i32, ptr @GInt3, align 4 -; CGSCC-NEXT: call void @useI32(i32 [[L0]]) +; CGSCC-NEXT: call void @useI32(i32 1) ; CGSCC-NEXT: store i32 1, ptr @GInt3, align 4 -; CGSCC-NEXT: [[L1:%.*]] = load i32, ptr @GInt3, align 4 -; CGSCC-NEXT: call void @useI32(i32 [[L1]]) +; 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 @@ -212,8 +207,7 @@ define void @entry3(i1 %c, i32 %v) { ; 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: [[L4:%.*]] = load i32, ptr @GInt3, align 4 -; CGSCC-NEXT: call void @useI32(i32 [[L4]]) +; CGSCC-NEXT: call void @useI32(i32 noundef 1) ; CGSCC-NEXT: ret void ; %l0 = load i32, ptr @GInt3 @@ -241,10 +235,9 @@ define void @entry4(i1 %c, i32 %v) { ; 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 [[L0]]) +; TUNIT-NEXT: call void @useI32(i32 noundef [[L0]]) ; TUNIT-NEXT: store i32 1, ptr @GInt4, align 4 -; TUNIT-NEXT: [[L1:%.*]] = load i32, ptr @GInt4, align 4 -; TUNIT-NEXT: call void @useI32(i32 [[L1]]) +; 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 @@ -255,18 +248,16 @@ define void @entry4(i1 %c, i32 %v) { ; 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: [[L4:%.*]] = load i32, ptr @GInt4, align 4 -; TUNIT-NEXT: call void @useI32(i32 [[L4]]) +; 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 [[L0]]) +; CGSCC-NEXT: call void @useI32(i32 noundef [[L0]]) ; CGSCC-NEXT: store i32 1, ptr @GInt4, align 4 -; CGSCC-NEXT: [[L1:%.*]] = load i32, ptr @GInt4, align 4 -; CGSCC-NEXT: call void @useI32(i32 [[L1]]) +; 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 @@ -277,8 +268,7 @@ define void @entry4(i1 %c, i32 %v) { ; 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: [[L4:%.*]] = load i32, ptr @GInt4, align 4 -; CGSCC-NEXT: call void @useI32(i32 [[L4]]) +; CGSCC-NEXT: call void @useI32(i32 noundef 1) ; CGSCC-NEXT: ret void ; %l0 = load i32, ptr @GInt4 @@ -306,11 +296,9 @@ 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: [[L0:%.*]] = load i32, ptr @GInt5, align 4 -; TUNIT-NEXT: call void @useI32(i32 [[L0]]) +; TUNIT-NEXT: call void @useI32(i32 1) ; TUNIT-NEXT: store i32 1, ptr @GInt5, align 4 -; TUNIT-NEXT: [[L1:%.*]] = load i32, ptr @GInt5, align 4 -; TUNIT-NEXT: call void @useI32(i32 [[L1]]) #[[ATTR6:[0-9]+]] +; 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 @@ -321,18 +309,15 @@ define void @entry5(i1 %c, i32 %v) { ; 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: [[L4:%.*]] = load i32, ptr @GInt5, align 4 -; TUNIT-NEXT: call void @useI32(i32 [[L4]]) #[[ATTR6]] +; 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: [[L0:%.*]] = load i32, ptr @GInt5, align 4 -; CGSCC-NEXT: call void @useI32(i32 [[L0]]) +; CGSCC-NEXT: call void @useI32(i32 1) ; CGSCC-NEXT: store i32 1, ptr @GInt5, align 4 -; CGSCC-NEXT: [[L1:%.*]] = load i32, ptr @GInt5, align 4 -; CGSCC-NEXT: call void @useI32(i32 [[L1]]) #[[ATTR7:[0-9]+]] +; 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 @@ -343,8 +328,7 @@ define void @entry5(i1 %c, i32 %v) { ; 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: [[L4:%.*]] = load i32, ptr @GInt5, align 4 -; CGSCC-NEXT: call void @useI32(i32 [[L4]]) #[[ATTR7]] +; CGSCC-NEXT: call void @useI32(i32 noundef 1) #[[ATTR7]] ; CGSCC-NEXT: ret void ; %l0 = load i32, ptr @GInt5 @@ -727,8 +711,7 @@ define internal void @exclusion_set3_helper(i1 %c, ptr %p) { ; TUNIT-NEXT: call void @usei32(i32 [[USE2]]) ; TUNIT-NEXT: br label [[T]] ; TUNIT: m: -; TUNIT-NEXT: [[USE3:%.*]] = load i32, ptr [[P]], align 4 -; TUNIT-NEXT: call void @usei32(i32 [[USE3]]) +; TUNIT-NEXT: call void @usei32(i32 42) ; TUNIT-NEXT: ret void ; ; CGSCC: Function Attrs: nosync diff --git a/llvm/unittests/Transforms/IPO/AttributorTest.cpp b/llvm/unittests/Transforms/IPO/AttributorTest.cpp index cd289a1..4a38684 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. -- 2.7.4