//===- 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 CondGuardInst;
 
class Function;
 
class raw_ostream;
 
class TargetTransformInfo;
 
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;
 
 
 
  TargetTransformInfo *TTI;
 
 
 
  /// 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, TargetTransformInfo *TTI = nullptr)
 
      : F(F), TTI(TTI) {}
 
 
 
  /// 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(CondGuardInst *CI);
 
 
 
  /// Remove an \@llvm.assume intrinsic from this function's cache if it has
 
  /// been added to the cache earlier.
 
  void unregisterAssumption(CondGuardInst *CI);
 
 
 
  /// Update the cache of values being affected by this assumption (i.e.
 
  /// the values about which this assumption provides information).
 
  void updateAffectedValues(CondGuardInst *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 &);
 
};
 
 
 
/// 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