123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- #pragma once
- #ifdef __GNUC__
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wunused-parameter"
- #endif
- //===- llvm/Analysis/AssumptionCache.h - Track @llvm.assume -----*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // This file contains a pass that keeps track of @llvm.assume intrinsics in
- // the functions of a module (allowing assumptions within any function to be
- // found cheaply by other parts of the optimizer).
- //
- //===----------------------------------------------------------------------===//
- #ifndef LLVM_ANALYSIS_ASSUMPTIONCACHE_H
- #define LLVM_ANALYSIS_ASSUMPTIONCACHE_H
- #include "llvm/ADT/ArrayRef.h"
- #include "llvm/ADT/DenseMap.h"
- #include "llvm/ADT/DenseMapInfo.h"
- #include "llvm/ADT/SmallVector.h"
- #include "llvm/IR/PassManager.h"
- #include "llvm/IR/ValueHandle.h"
- #include "llvm/Pass.h"
- #include <memory>
- namespace llvm {
- class CallInst;
- class Function;
- class raw_ostream;
- class Value;
- /// A cache of \@llvm.assume calls within a function.
- ///
- /// This cache provides fast lookup of assumptions within a function by caching
- /// them and amortizing the cost of scanning for them across all queries. Passes
- /// that create new assumptions are required to call registerAssumption() to
- /// register any new \@llvm.assume calls that they create. Deletions of
- /// \@llvm.assume calls do not require special handling.
- class AssumptionCache {
- public:
- /// Value of ResultElem::Index indicating that the argument to the call of the
- /// llvm.assume.
- enum : unsigned { ExprResultIdx = std::numeric_limits<unsigned>::max() };
- struct ResultElem {
- WeakVH Assume;
- /// contains either ExprResultIdx or the index of the operand bundle
- /// containing the knowledge.
- unsigned Index;
- operator Value *() const { return Assume; }
- };
- private:
- /// The function for which this cache is handling assumptions.
- ///
- /// We track this to lazily populate our assumptions.
- Function &F;
- /// Vector of weak value handles to calls of the \@llvm.assume
- /// intrinsic.
- SmallVector<ResultElem, 4> AssumeHandles;
- class AffectedValueCallbackVH final : public CallbackVH {
- AssumptionCache *AC;
- void deleted() override;
- void allUsesReplacedWith(Value *) override;
- public:
- using DMI = DenseMapInfo<Value *>;
- AffectedValueCallbackVH(Value *V, AssumptionCache *AC = nullptr)
- : CallbackVH(V), AC(AC) {}
- };
- friend AffectedValueCallbackVH;
- /// A map of values about which an assumption might be providing
- /// information to the relevant set of assumptions.
- using AffectedValuesMap =
- DenseMap<AffectedValueCallbackVH, SmallVector<ResultElem, 1>,
- AffectedValueCallbackVH::DMI>;
- AffectedValuesMap AffectedValues;
- /// Get the vector of assumptions which affect a value from the cache.
- SmallVector<ResultElem, 1> &getOrInsertAffectedValues(Value *V);
- /// Move affected values in the cache for OV to be affected values for NV.
- void transferAffectedValuesInCache(Value *OV, Value *NV);
- /// Flag tracking whether we have scanned the function yet.
- ///
- /// We want to be as lazy about this as possible, and so we scan the function
- /// at the last moment.
- bool Scanned = false;
- /// Scan the function for assumptions and add them to the cache.
- void scanFunction();
- public:
- /// Construct an AssumptionCache from a function by scanning all of
- /// its instructions.
- AssumptionCache(Function &F) : F(F) {}
- /// This cache is designed to be self-updating and so it should never be
- /// invalidated.
- bool invalidate(Function &, const PreservedAnalyses &,
- FunctionAnalysisManager::Invalidator &) {
- return false;
- }
- /// Add an \@llvm.assume intrinsic to this function's cache.
- ///
- /// The call passed in must be an instruction within this function and must
- /// not already be in the cache.
- void registerAssumption(CallInst *CI);
- /// Remove an \@llvm.assume intrinsic from this function's cache if it has
- /// been added to the cache earlier.
- void unregisterAssumption(CallInst *CI);
- /// Update the cache of values being affected by this assumption (i.e.
- /// the values about which this assumption provides information).
- void updateAffectedValues(CallInst *CI);
- /// Clear the cache of \@llvm.assume intrinsics for a function.
- ///
- /// It will be re-scanned the next time it is requested.
- void clear() {
- AssumeHandles.clear();
- AffectedValues.clear();
- Scanned = false;
- }
- /// Access the list of assumption handles currently tracked for this
- /// function.
- ///
- /// Note that these produce weak handles that may be null. The caller must
- /// handle that case.
- /// FIXME: We should replace this with pointee_iterator<filter_iterator<...>>
- /// when we can write that to filter out the null values. Then caller code
- /// will become simpler.
- MutableArrayRef<ResultElem> assumptions() {
- if (!Scanned)
- scanFunction();
- return AssumeHandles;
- }
- /// Access the list of assumptions which affect this value.
- MutableArrayRef<ResultElem> assumptionsFor(const Value *V) {
- if (!Scanned)
- scanFunction();
- auto AVI = AffectedValues.find_as(const_cast<Value *>(V));
- if (AVI == AffectedValues.end())
- return MutableArrayRef<ResultElem>();
- return AVI->second;
- }
- };
- /// A function analysis which provides an \c AssumptionCache.
- ///
- /// This analysis is intended for use with the new pass manager and will vend
- /// assumption caches for a given function.
- class AssumptionAnalysis : public AnalysisInfoMixin<AssumptionAnalysis> {
- friend AnalysisInfoMixin<AssumptionAnalysis>;
- static AnalysisKey Key;
- public:
- using Result = AssumptionCache;
- AssumptionCache run(Function &F, FunctionAnalysisManager &) {
- return AssumptionCache(F);
- }
- };
- /// Printer pass for the \c AssumptionAnalysis results.
- class AssumptionPrinterPass : public PassInfoMixin<AssumptionPrinterPass> {
- raw_ostream &OS;
- public:
- explicit AssumptionPrinterPass(raw_ostream &OS) : OS(OS) {}
- PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
- };
- /// An immutable pass that tracks lazily created \c AssumptionCache
- /// objects.
- ///
- /// This is essentially a workaround for the legacy pass manager's weaknesses
- /// which associates each assumption cache with Function and clears it if the
- /// function is deleted. The nature of the AssumptionCache is that it is not
- /// invalidated by any changes to the function body and so this is sufficient
- /// to be conservatively correct.
- class AssumptionCacheTracker : public ImmutablePass {
- /// A callback value handle applied to function objects, which we use to
- /// delete our cache of intrinsics for a function when it is deleted.
- class FunctionCallbackVH final : public CallbackVH {
- AssumptionCacheTracker *ACT;
- void deleted() override;
- public:
- using DMI = DenseMapInfo<Value *>;
- FunctionCallbackVH(Value *V, AssumptionCacheTracker *ACT = nullptr)
- : CallbackVH(V), ACT(ACT) {}
- };
- friend FunctionCallbackVH;
- using FunctionCallsMap =
- DenseMap<FunctionCallbackVH, std::unique_ptr<AssumptionCache>,
- FunctionCallbackVH::DMI>;
- FunctionCallsMap AssumptionCaches;
- public:
- /// Get the cached assumptions for a function.
- ///
- /// If no assumptions are cached, this will scan the function. Otherwise, the
- /// existing cache will be returned.
- AssumptionCache &getAssumptionCache(Function &F);
- /// Return the cached assumptions for a function if it has already been
- /// scanned. Otherwise return nullptr.
- AssumptionCache *lookupAssumptionCache(Function &F);
- AssumptionCacheTracker();
- ~AssumptionCacheTracker() override;
- void releaseMemory() override {
- verifyAnalysis();
- AssumptionCaches.shrink_and_clear();
- }
- void verifyAnalysis() const override;
- bool doFinalization(Module &) override {
- verifyAnalysis();
- return false;
- }
- static char ID; // Pass identification, replacement for typeid
- };
- template<> struct simplify_type<AssumptionCache::ResultElem> {
- using SimpleType = Value *;
- static SimpleType getSimplifiedValue(AssumptionCache::ResultElem &Val) {
- return Val;
- }
- };
- template<> struct simplify_type<const AssumptionCache::ResultElem> {
- using SimpleType = /*const*/ Value *;
- static SimpleType getSimplifiedValue(const AssumptionCache::ResultElem &Val) {
- return Val;
- }
- };
- } // end namespace llvm
- #endif // LLVM_ANALYSIS_ASSUMPTIONCACHE_H
- #ifdef __GNUC__
- #pragma GCC diagnostic pop
- #endif
|