[llvm] [KnownBitsDataflow] Introduce new analysis (PR #177616)

Ramkumar Ramachandra via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 30 01:01:34 PDT 2026


https://github.com/artagnon updated https://github.com/llvm/llvm-project/pull/177616

>From 9799b68b7ec46d572d6acd4a229382ba49342bb4 Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Fri, 25 Sep 2026 10:17:41 +0100
Subject: [PATCH] [KnownBitsDataflow] Introduce new analysis

---
 .../include/llvm/Analysis/KnownBitsDataflow.h | 245 ++++++++
 llvm/include/llvm/InitializePasses.h          |   2 +
 .../Transforms/InstCombine/InstCombiner.h     |  27 +-
 llvm/lib/Analysis/Analysis.cpp                |   1 +
 llvm/lib/Analysis/CMakeLists.txt              |   1 +
 llvm/lib/Analysis/KnownBitsDataflow.cpp       | 147 +++++
 llvm/lib/Passes/PassBuilder.cpp               |   1 +
 llvm/lib/Passes/PassRegistry.def              |   1 +
 .../InstCombine/InstCombineInternal.h         |  14 +-
 .../InstCombine/InstructionCombining.cpp      |  26 +-
 llvm/unittests/Analysis/CMakeLists.txt        |   1 +
 .../Analysis/KnownBitsDataflowTest.cpp        | 533 ++++++++++++++++++
 12 files changed, 975 insertions(+), 24 deletions(-)
 create mode 100644 llvm/include/llvm/Analysis/KnownBitsDataflow.h
 create mode 100644 llvm/lib/Analysis/KnownBitsDataflow.cpp
 create mode 100644 llvm/unittests/Analysis/KnownBitsDataflowTest.cpp

diff --git a/llvm/include/llvm/Analysis/KnownBitsDataflow.h b/llvm/include/llvm/Analysis/KnownBitsDataflow.h
new file mode 100644
index 0000000000000..bc919c256086c
--- /dev/null
+++ b/llvm/include/llvm/Analysis/KnownBitsDataflow.h
@@ -0,0 +1,245 @@
+//===- KnownBitsDataflow.h - Cache and invalidate KnownBits ---------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// Caches KnownBits for Values, aand invalidates the cached results on IR
+// updates by walking the dataflow graph. Provides a custom Map-like container
+// with lookup and insertion APIs.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ANALYSIS_KNOWNBITSDATAFLOW_H
+#define LLVM_ANALYSIS_KNOWNBITSDATAFLOW_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DepthFirstIterator.h"
+#include "llvm/ADT/GraphTraits.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/IR/Constant.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/IR/Value.h"
+#include "llvm/IR/ValueHandle.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/KnownBits.h"
+#include <memory>
+
+namespace llvm {
+class Function;
+class DataLayout;
+class raw_ostream;
+class KnownBitsDataflow;
+
+/// GraphTraits enabling depth_first over Values.
+template <typename NodeRef, typename ChildIteratorType>
+struct NodeGraphTraitsBase {
+  static NodeRef getEntryNode(NodeRef N) { return N; }
+  static ChildIteratorType child_begin(NodeRef N) { // NOLINT
+    return N->user_begin();
+  }
+  static ChildIteratorType child_end(NodeRef N) { // NOLINT
+    return N->user_end();
+  }
+};
+
+template <>
+struct GraphTraits<Value *>
+    : public NodeGraphTraitsBase<Value *, Value::user_iterator> {
+  using NodeRef = Value *;
+  using ChildIteratorType = Value::user_iterator;
+};
+
+/// A custom ValueHandle with callback to erase KnownBits in the cache when an
+/// Instruction is deleted, or invalidate dependent KnownBits when it is
+/// RAUW'ed.
+class KnownBitsVH : private CallbackVH {
+  friend class KnownBitsDataflow;
+  KnownBitsDataflow *KBD;
+
+  virtual void anchor() override;
+  virtual void deleted() override;
+  virtual void allUsesReplacedWith(Value *New) override;
+
+public:
+  KnownBitsVH(const Value *V, KnownBitsDataflow *KBD)
+      : CallbackVH(V), KBD(KBD) {}
+  virtual ~KnownBitsVH() = default;
+  using ValueHandleBase::getValPtr;
+  using CallbackVH::operator Value *;
+  bool operator==(const KnownBitsVH &Other) const {
+    return getValPtr() == Other.getValPtr();
+  }
+  bool operator<(const KnownBitsVH &Other) const {
+    return getValPtr() < Other.getValPtr();
+  }
+};
+
+// The DenseMapInfo for our KnownBits ValueHandle is just the DenseMapInfo on
+// the Value pointer: there is no other information in the ValueHandle that's
+// relevant for a DenseMap.
+template <> struct DenseMapInfo<KnownBitsVH> {
+  static unsigned getHashValue(const KnownBitsVH &Val) {
+    return DenseMapInfo<const Value *>::getHashValue(Val.getValPtr());
+  }
+
+  static bool isEqual(const KnownBitsVH &LHS, const KnownBitsVH &RHS) {
+    return DenseMapInfo<const Value *>::isEqual(LHS.getValPtr(),
+                                                RHS.getValPtr());
+  }
+};
+
+/// A DenseMap holding a ValueHandle, that performs lookups based on the
+/// underlying Value.
+template <typename ValueT>
+using DenseMapForVH =
+    DenseMap<KnownBitsVH, ValueT, DenseMapInfo<const Value *>>;
+
+/// The ValueT of our DenseMap is actually a KnownBits augmented with
+/// context-instruction information.
+struct KnownBitsWithCtxI : public KnownBits {
+  WeakVH CtxI;
+  KnownBitsWithCtxI() = default;
+  KnownBitsWithCtxI(const KnownBits &Known, const Instruction *CtxI)
+      : KnownBits(Known), CtxI(const_cast<Instruction *>(CtxI)) {}
+  bool canUseWith(const Instruction *Other) const {
+    // If the cached value was computed with a CtxI, and one without a CxtI is
+    // requested, returning the cached value would yield a better optimization
+    // result.
+    return !Other || Other == CtxI;
+  }
+};
+
+/// A structure keeps a mapping between a custom ValueHandle and
+/// KnownBitsWithCtxI, with core functionality to cache KnownBits with automatic
+/// invalidation on IR manipulation. We compute a deterministic ordering for
+/// entries in the map for testing and debugging.
+class LLVM_ABI KnownBitsDataflow : protected DenseMapForVH<KnownBitsWithCtxI> {
+  friend class KnownBitsVH;
+
+  /// Do a forward data-flow walk, and find all ValueHandles whose KnownBits
+  /// depeends on the KnownBits of \p V. Returns a range of Values.
+  auto forwardDataflow(const KnownBitsVH &V) const {
+    return make_filter_range(depth_first(V.getValPtr()),
+                             bind_front(&KnownBitsDataflow::contains, this));
+  }
+
+protected:
+  using BaseT = DenseMapForVH<KnownBitsWithCtxI>;
+
+  LLVM_ABI_FOR_TEST KnownBitsVH key_as(const Value *V) const { // NOLINT
+    auto It = find_as(V);
+    assert(It != end() && "Expected to find ValueHandle");
+    return It->first;
+  }
+  LLVM_ABI_FOR_TEST KnownBitsWithCtxI &value_as(const Value *V) { // NOLINT
+    auto It = find_as(V);
+    assert(It != end() && "Expected to find ValueHandle");
+    return It->second;
+  }
+  LLVM_ABI_FOR_TEST KnownBitsWithCtxI value_as(const Value *V) const { // NOLINT
+    auto It = find_as(V);
+    assert(It != end() && "Expected to find ValueHandle");
+    return It->second;
+  }
+
+  /// Invalidates KnownBits in the entire subgraph found from the
+  /// forwardDataflow walk starting from \p V, turning them into Unknown values.
+  /// Triggered on IR manipulation events.
+  LLVM_ABI_FOR_TEST void invalidate(const KnownBitsVH &V) {
+    for (const Value *N : forwardDataflow(V))
+      value_as(N).resetAll();
+  }
+
+  /// Range-based variant of forwardDataflow used in print.
+  LLVM_ABI_FOR_TEST SmallVector<const Value *>
+  forwardDataflow(ArrayRef<KnownBitsVH> Roots) const;
+
+  /// Checks if \p V is present in the map.
+  LLVM_ABI_FOR_TEST bool contains(const Value *V) const {
+    return find_as(V) != end();
+  }
+
+  /// Roots are the function \p F's arguments, along with Instructions that
+  /// expose a new root like phis and fptosi. This is used in print, to print
+  /// entries in the map in deterministic order.
+  LLVM_ABI_FOR_TEST SmallVector<KnownBitsVH>
+  computeRoots(const Function &F) const;
+
+public:
+  LLVM_ABI KnownBitsDataflow() {}
+  LLVM_ABI KnownBitsDataflow(const KnownBitsDataflow &) = delete;
+  LLVM_ABI KnownBitsDataflow &operator=(const KnownBitsDataflow &) = delete;
+
+  /// A small helper extracted from ValueTracking.
+  LLVM_ABI static unsigned getBitWidth(Type *Ty, const DataLayout &DL);
+
+  using BaseT::empty;
+  using BaseT::size;
+
+  /// Checks if \p V if it is present in the map, and if it has a valid
+  /// (non-Unknown) KnownBits, returning it if so. Pass \p CtxI to filter on
+  /// compatibility of context-instructions.
+  std::optional<KnownBits>
+      LLVM_ABI lookup(const Value *V, const Instruction *CtxI = nullptr) const {
+    // Constants should never be inserted into the map. This is the fast
+    // lookup-path.
+    if (isa<Constant>(V))
+      return std::nullopt;
+    auto It = find_as(V);
+    if (It == end())
+      return std::nullopt;
+    const KnownBitsWithCtxI &Known = It->second;
+    if (Known.isUnknown() || !Known.canUseWith(CtxI))
+      return std::nullopt;
+    return Known;
+  }
+
+  /// Registers that \p V has KnownBits information \p Known, with
+  /// context-instruction \p CtxI, overwriting any existing value. Is a no-op on
+  /// constant \p V and unknown \p Known.
+  void LLVM_ABI emplace_as(const Value *V, const KnownBits &Known, // NOLINT
+                           const Instruction *CtxI = nullptr) {
+    if (isa<Constant>(V) || Known.isUnknown())
+      return;
+    emplace_or_assign({V, this}, KnownBitsWithCtxI(Known, CtxI));
+  }
+
+  /// This routine prints in the entries in the map in deterministic order.
+  LLVM_ABI void print(const Function &F, raw_ostream &OS) const;
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+  LLVM_DUMP_METHOD void dump(const Function &F) const;
+#endif
+
+  bool LLVM_ABI invalidate(Function &, const PreservedAnalyses &PA,
+                           FunctionAnalysisManager::Invalidator &);
+};
+
+class LLVM_ABI KnownBitsDataflowAnalysis
+    : public AnalysisInfoMixin<KnownBitsDataflowAnalysis> {
+public:
+  static AnalysisKey Key;
+  using Result = KnownBitsDataflow;
+  KnownBitsDataflow run(Function &F, FunctionAnalysisManager &);
+};
+
+/// Legacy PM wrapper pass.
+class LLVM_ABI KnownBitsDataflowAnalysisWrapperPass : public FunctionPass {
+  std::unique_ptr<KnownBitsDataflow> Result;
+
+public:
+  static char ID;
+
+  KnownBitsDataflowAnalysisWrapperPass();
+  KnownBitsDataflow &getResult() { return *Result; }
+  void getAnalysisUsage(AnalysisUsage &AU) const override;
+  bool runOnFunction(Function &F) override;
+};
+} // end namespace llvm
+
+#endif // LLVM_ANALYSIS_KNOWNBITSDATAFLOW_H
diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h
index cb5a5a2dfb306..01e9b2012c2ef 100644
--- a/llvm/include/llvm/InitializePasses.h
+++ b/llvm/include/llvm/InitializePasses.h
@@ -147,6 +147,8 @@ LLVM_ABI void initializeInstructionSelectLegacyPass(PassRegistry &);
 LLVM_ABI void initializeInterleavedAccessPass(PassRegistry &);
 LLVM_ABI void initializeInterleavedLoadCombinePass(PassRegistry &);
 LLVM_ABI void initializeJMCInstrumenterPass(PassRegistry &);
+LLVM_ABI void
+initializeKnownBitsDataflowAnalysisWrapperPassPass(PassRegistry &);
 LLVM_ABI void initializeMachineKCFILegacyPass(PassRegistry &);
 LLVM_ABI void initializeLCSSAVerificationPassPass(PassRegistry &);
 LLVM_ABI void initializeLCSSAWrapperPassPass(PassRegistry &);
diff --git a/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h b/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
index 636b9a6278d87..05f7fdc707bca 100644
--- a/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
+++ b/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
@@ -21,6 +21,7 @@
 #include "llvm/ADT/PostOrderIterator.h"
 #include "llvm/Analysis/DomConditionCache.h"
 #include "llvm/Analysis/InstructionSimplify.h"
+#include "llvm/Analysis/KnownBitsDataflow.h"
 #include "llvm/Analysis/TargetFolder.h"
 #include "llvm/Analysis/ValueTracking.h"
 #include "llvm/IR/IRBuilder.h"
@@ -97,6 +98,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombiner {
   BranchProbabilityInfo *BPI;
   ProfileSummaryInfo *PSI;
   DomConditionCache DC;
+  KnownBitsDataflow &KBD;
 
   ReversePostOrderTraversal<BasicBlock *> &RPOT;
 
@@ -119,11 +121,11 @@ class LLVM_LIBRARY_VISIBILITY InstCombiner {
 
 public:
   InstCombiner(InstructionWorklist &Worklist, Function &F, AAResults *AA,
-               AssumptionCache &AC, TargetLibraryInfo &TLI,
-               TargetTransformInfo &TTI, DominatorTree &DT,
-               OptimizationRemarkEmitter &ORE, BlockFrequencyInfo *BFI,
-               BranchProbabilityInfo *BPI, ProfileSummaryInfo *PSI,
-               const DataLayout &DL,
+               AssumptionCache &AC, KnownBitsDataflow &KBD,
+               TargetLibraryInfo &TLI, TargetTransformInfo &TTI,
+               DominatorTree &DT, OptimizationRemarkEmitter &ORE,
+               BlockFrequencyInfo *BFI, BranchProbabilityInfo *BPI,
+               ProfileSummaryInfo *PSI, const DataLayout &DL,
                ReversePostOrderTraversal<BasicBlock *> &RPOT)
       : TTIForTargetIntrinsicsOnly(TTI),
         Builder(F.getContext(), TargetFolder(DL),
@@ -132,7 +134,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombiner {
         TLI(TLI), DT(DT), DL(DL),
         SQ(DL, &TLI, &DT, &AC, nullptr, /*UseInstrInfo*/ true,
            /*CanUseUndef*/ true, &DC),
-        ORE(ORE), BFI(BFI), BPI(BPI), PSI(PSI), RPOT(RPOT) {}
+        ORE(ORE), BFI(BFI), BPI(BPI), PSI(PSI), KBD(KBD), RPOT(RPOT) {}
 
   virtual ~InstCombiner() = default;
 
@@ -467,12 +469,23 @@ class LLVM_LIBRARY_VISIBILITY InstCombiner {
 
   void computeKnownBits(const Value *V, KnownBits &Known,
                         const Instruction *CtxI, unsigned Depth = 0) const {
+    if (auto K = KBD.lookup(V, CtxI)) {
+      Known = *K;
+      return;
+    }
     llvm::computeKnownBits(V, Known, SQ.getWithInstruction(CtxI), Depth);
+
+    // Depth is a monotonically increasing variable. Hence, earlier computations
+    // are always more refined than later ones, unless a literal hardcoded Depth
+    // variable is passed to this function, which never happens.
+    KBD.emplace_as(V, Known, CtxI);
   }
 
   KnownBits computeKnownBits(const Value *V, const Instruction *CtxI,
                              unsigned Depth = 0) const {
-    return llvm::computeKnownBits(V, SQ.getWithInstruction(CtxI), Depth);
+    KnownBits Known(KnownBitsDataflow::getBitWidth(V->getType(), SQ.DL));
+    computeKnownBits(V, Known, CtxI, Depth);
+    return Known;
   }
 
   bool isKnownToBeAPowerOfTwo(const Value *V, bool OrZero = false,
diff --git a/llvm/lib/Analysis/Analysis.cpp b/llvm/lib/Analysis/Analysis.cpp
index 9eec1924e797e..6f59f9a328cd8 100644
--- a/llvm/lib/Analysis/Analysis.cpp
+++ b/llvm/lib/Analysis/Analysis.cpp
@@ -45,6 +45,7 @@ void llvm::initializeAnalysis(PassRegistry &Registry) {
   initializeExternalAAWrapperPassPass(Registry);
   initializeImmutableModuleSummaryIndexWrapperPassPass(Registry);
   initializeIVUsersWrapperPassPass(Registry);
+  initializeKnownBitsDataflowAnalysisWrapperPassPass(Registry);
   initializeLazyBranchProbabilityInfoPassPass(Registry);
   initializeLazyBFIPassPass(Registry);
   initializeLazyBlockFrequencyInfoPassPass(Registry);
diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt
index 73250fc99e5a7..059c5d2d7000a 100644
--- a/llvm/lib/Analysis/CMakeLists.txt
+++ b/llvm/lib/Analysis/CMakeLists.txt
@@ -108,6 +108,7 @@ add_llvm_component_library(LLVMAnalysis
   InstructionSimplify.cpp
   InteractiveModelRunner.cpp
   KernelInfo.cpp
+  KnownBitsDataflow.cpp
   LastRunTrackingAnalysis.cpp
   LazyBranchProbabilityInfo.cpp
   LazyBlockFrequencyInfo.cpp
diff --git a/llvm/lib/Analysis/KnownBitsDataflow.cpp b/llvm/lib/Analysis/KnownBitsDataflow.cpp
new file mode 100644
index 0000000000000..d9a2e79b4d723
--- /dev/null
+++ b/llvm/lib/Analysis/KnownBitsDataflow.cpp
@@ -0,0 +1,147 @@
+//===- KnownBitsDataflow.cpp - Cache and invalidate KnownBits -------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Analysis/KnownBitsDataflow.h"
+#include "llvm/ADT/SetVector.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/Function.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "known-bits-dataflow"
+
+// Pin the vtable.
+void KnownBitsVH::anchor() {}
+
+void KnownBitsVH::deleted() {
+  // This is called in the destructor of ValueHandleBase. Carefully avoid
+  // constructing a new ValueHandle, and avoid calling virtual functions.
+  [[maybe_unused]] bool Removed = KBD->remove_if(
+      [&](const auto &It) { return It.first.getValPtr() == getValPtr(); });
+  assert(Removed && "Expected to find ValPtr in map");
+  clearValPtr();
+}
+
+void KnownBitsVH::allUsesReplacedWith(Value *New) {
+  // This is called in ValueHandleBase before any uses are replaced.
+  KBD->invalidate(*this);
+  setValPtr(New);
+}
+
+/// A wrapper around make_filter_range, that filters \p R on scalar types that
+/// are either integer or pointer type, as these are the only types handled by
+/// computeKnownBits.
+template <typename RangeT>
+static auto make_knownbits_range(RangeT &&R) { // NOLINT
+  return make_filter_range(R, [](const auto &V) {
+    return V->getType()->getScalarType()->isIntOrPtrTy();
+  });
+}
+
+unsigned KnownBitsDataflow::getBitWidth(Type *Ty, const DataLayout &DL) {
+  if (unsigned BitWidth = Ty->getScalarSizeInBits())
+    return BitWidth;
+  return DL.getPointerTypeSizeInBits(Ty);
+}
+
+LLVM_ABI_FOR_TEST SmallVector<const Value *>
+KnownBitsDataflow::forwardDataflow(ArrayRef<KnownBitsVH> Roots) const {
+  SetVector<const Value *> Collected;
+  for (const KnownBitsVH &V : Roots)
+    Collected.insert_range(forwardDataflow(V));
+  return Collected.takeVector();
+}
+
+SmallVector<KnownBitsVH>
+KnownBitsDataflow::computeRoots(const Function &F) const {
+  SmallVector<KnownBitsVH> Roots;
+
+  // First, collect function arguments.
+  for (const Value *V : make_knownbits_range(make_pointer_range(F.args())))
+    if (contains(V))
+      Roots.emplace_back(key_as(V));
+
+  // A helper to find out whether a Value is reachable from Roots that computes
+  // the reachability information just in time, as Roots are updated.
+  auto IsReachableFromRoots = [&](const Value *V) {
+    for (const KnownBitsVH &R : Roots)
+      for (const Value *N : make_knownbits_range(depth_first(R.getValPtr())))
+        if (N == V)
+          return true;
+    return false;
+  };
+
+  // Now collect all Instructions that aren't reachable from the function's
+  // arguments, updating Roots, as we test for unreachability.
+  for (const BasicBlock &BB : F)
+    for (const Value *V : make_knownbits_range(make_pointer_range(BB)))
+      if (!IsReachableFromRoots(V) && contains(V))
+        Roots.emplace_back(key_as(V));
+
+  return Roots;
+}
+
+void KnownBitsDataflow::print(const Function &F, raw_ostream &OS) const {
+  auto IsLeaf = [](const Value *V) {
+    return make_knownbits_range(V->users()).empty();
+  };
+  SmallVector<KnownBitsVH> Roots = computeRoots(F);
+  for (const Value *V : forwardDataflow(Roots)) {
+    if (is_contained(Roots, V))
+      OS << "^ ";
+    else if (IsLeaf(V))
+      OS << "$ ";
+    else
+      OS << "  ";
+    V->print(OS);
+    OS << " | ";
+    value_as(V).print(OS);
+    OS << "\n";
+  }
+}
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void KnownBitsDataflow::dump(const Function &F) const {
+  print(F, dbgs());
+}
+#endif
+
+bool KnownBitsDataflow::invalidate(Function &, const PreservedAnalyses &PA,
+                                   FunctionAnalysisManager::Invalidator &) {
+  auto PAC = PA.getChecker<KnownBitsDataflowAnalysis>();
+  return !PAC.preserved();
+}
+
+AnalysisKey KnownBitsDataflowAnalysis::Key;
+
+KnownBitsDataflow KnownBitsDataflowAnalysis::run(Function &F,
+                                                 FunctionAnalysisManager &) {
+  return {};
+}
+
+// Legacy PM wrapper pass.
+char KnownBitsDataflowAnalysisWrapperPass::ID = 0;
+
+KnownBitsDataflowAnalysisWrapperPass::KnownBitsDataflowAnalysisWrapperPass()
+    : FunctionPass(ID) {}
+
+void KnownBitsDataflowAnalysisWrapperPass::getAnalysisUsage(
+    AnalysisUsage &AU) const {
+  AU.setPreservesAll();
+}
+
+bool KnownBitsDataflowAnalysisWrapperPass::runOnFunction(Function &F) {
+  Result.reset(new KnownBitsDataflow());
+  return false;
+}
+
+INITIALIZE_PASS(KnownBitsDataflowAnalysisWrapperPass, "known-bits-dataflow",
+                "KnownBits Dataflow", false, true)
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 30a6f75a1b86d..d4e8b920909af 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -48,6 +48,7 @@
 #include "llvm/Analysis/InlineAdvisor.h"
 #include "llvm/Analysis/InstCount.h"
 #include "llvm/Analysis/KernelInfo.h"
+#include "llvm/Analysis/KnownBitsDataflow.h"
 #include "llvm/Analysis/LastRunTrackingAnalysis.h"
 #include "llvm/Analysis/LazyCallGraph.h"
 #include "llvm/Analysis/LazyValueInfo.h"
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index af4ce5029551d..3762162eba219 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -367,6 +367,7 @@ FUNCTION_ANALYSIS("ephemerals", EphemeralValuesAnalysis())
 FUNCTION_ANALYSIS("func-properties", FunctionPropertiesAnalysis())
 FUNCTION_ANALYSIS("machine-function-info", MachineFunctionAnalysis(*TM))
 FUNCTION_ANALYSIS("gc-function", GCFunctionAnalysis())
+FUNCTION_ANALYSIS("known-bits-dataflow", KnownBitsDataflowAnalysis())
 FUNCTION_ANALYSIS("last-run-tracking", LastRunTrackingAnalysis())
 FUNCTION_ANALYSIS("lazy-value-info", LazyValueAnalysis())
 FUNCTION_ANALYSIS("loops", LoopAnalysis())
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index 6e6c8c83e1eed..21df70513b307 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -72,14 +72,14 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
       public InstVisitor<InstCombinerImpl, Instruction *> {
 public:
   InstCombinerImpl(InstructionWorklist &Worklist, Function &F, AAResults *AA,
-                   AssumptionCache &AC, TargetLibraryInfo &TLI,
-                   TargetTransformInfo &TTI, DominatorTree &DT,
-                   OptimizationRemarkEmitter &ORE, BlockFrequencyInfo *BFI,
-                   BranchProbabilityInfo *BPI, ProfileSummaryInfo *PSI,
-                   const DataLayout &DL,
+                   AssumptionCache &AC, KnownBitsDataflow &KBD,
+                   TargetLibraryInfo &TLI, TargetTransformInfo &TTI,
+                   DominatorTree &DT, OptimizationRemarkEmitter &ORE,
+                   BlockFrequencyInfo *BFI, BranchProbabilityInfo *BPI,
+                   ProfileSummaryInfo *PSI, const DataLayout &DL,
                    ReversePostOrderTraversal<BasicBlock *> &RPOT)
-      : InstCombiner(Worklist, F, AA, AC, TLI, TTI, DT, ORE, BFI, BPI, PSI, DL,
-                     RPOT) {}
+      : InstCombiner(Worklist, F, AA, AC, KBD, TLI, TTI, DT, ORE, BFI, BPI, PSI,
+                     DL, RPOT) {}
 
   ~InstCombinerImpl() override = default;
 
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 3ca4974a2d661..a186f0e3aef80 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -48,6 +48,7 @@
 #include "llvm/Analysis/ConstantFolding.h"
 #include "llvm/Analysis/GlobalsModRef.h"
 #include "llvm/Analysis/InstructionSimplify.h"
+#include "llvm/Analysis/KnownBitsDataflow.h"
 #include "llvm/Analysis/LastRunTrackingAnalysis.h"
 #include "llvm/Analysis/LazyBlockFrequencyInfo.h"
 #include "llvm/Analysis/MemoryBuiltins.h"
@@ -6179,10 +6180,10 @@ void InstCombiner::computeBackEdges() {
 
 static bool combineInstructionsOverFunction(
     Function &F, InstructionWorklist &Worklist, AliasAnalysis *AA,
-    AssumptionCache &AC, TargetLibraryInfo &TLI, TargetTransformInfo &TTI,
-    DominatorTree &DT, OptimizationRemarkEmitter &ORE, BlockFrequencyInfo *BFI,
-    BranchProbabilityInfo *BPI, ProfileSummaryInfo *PSI,
-    const InstCombineOptions &Opts) {
+    AssumptionCache &AC, KnownBitsDataflow &KBD, TargetLibraryInfo &TLI,
+    TargetTransformInfo &TTI, DominatorTree &DT, OptimizationRemarkEmitter &ORE,
+    BlockFrequencyInfo *BFI, BranchProbabilityInfo *BPI,
+    ProfileSummaryInfo *PSI, const InstCombineOptions &Opts) {
   auto &DL = F.getDataLayout();
   bool VerifyFixpoint = Opts.VerifyFixpoint &&
                         !F.hasFnAttribute("instcombine-no-verify-fixpoint");
@@ -6210,8 +6211,8 @@ static bool combineInstructionsOverFunction(
     LLVM_DEBUG(dbgs() << "\n\nINSTCOMBINE ITERATION #" << Iteration << " on "
                       << F.getName() << "\n");
 
-    InstCombinerImpl IC(Worklist, F, AA, AC, TLI, TTI, DT, ORE, BFI, BPI, PSI,
-                        DL, RPOT);
+    InstCombinerImpl IC(Worklist, F, AA, AC, KBD, TLI, TTI, DT, ORE, BFI, BPI,
+                        PSI, DL, RPOT);
     IC.MaxArraySizeForCombine = MaxArraySize;
     bool MadeChangeInThisIteration = IC.prepareWorklist(F);
     MadeChangeInThisIteration |= IC.run();
@@ -6264,6 +6265,7 @@ PreservedAnalyses InstCombinePass::run(Function &F,
 
   auto &AC = AM.getResult<AssumptionAnalysis>(F);
   auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
+  auto &KBD = AM.getResult<KnownBitsDataflowAnalysis>(F);
   auto &TLI = AM.getResult<TargetLibraryAnalysis>(F);
   auto &ORE = AM.getResult<OptimizationRemarkEmitterAnalysis>(F);
   auto &TTI = AM.getResult<TargetIRAnalysis>(F);
@@ -6276,8 +6278,8 @@ PreservedAnalyses InstCombinePass::run(Function &F,
       &AM.getResult<BlockFrequencyAnalysis>(F) : nullptr;
   auto *BPI = AM.getCachedResult<BranchProbabilityAnalysis>(F);
 
-  if (!combineInstructionsOverFunction(F, Worklist, AA, AC, TLI, TTI, DT, ORE,
-                                       BFI, BPI, PSI, Options)) {
+  if (!combineInstructionsOverFunction(F, Worklist, AA, AC, KBD, TLI, TTI, DT,
+                                       ORE, BFI, BPI, PSI, Options)) {
     // No changes, all analyses are preserved.
     LRT.update(&ID, /*Changed=*/false);
     return PreservedAnalyses::all();
@@ -6295,6 +6297,7 @@ void InstructionCombiningPass::getAnalysisUsage(AnalysisUsage &AU) const {
   AU.setPreservesCFG();
   AU.addRequired<AAResultsWrapperPass>();
   AU.addRequired<AssumptionCacheTracker>();
+  AU.addRequired<KnownBitsDataflowAnalysisWrapperPass>();
   AU.addRequired<TargetLibraryInfoWrapperPass>();
   AU.addRequired<TargetTransformInfoWrapperPass>();
   AU.addRequired<DominatorTreeWrapperPass>();
@@ -6311,6 +6314,7 @@ bool InstructionCombiningPass::runOnFunction(Function &F) {
 
   // Required analyses.
   auto AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
+  auto &KBD = getAnalysis<KnownBitsDataflowAnalysisWrapperPass>().getResult();
   auto &AC = getAnalysis<AssumptionCacheTracker>().getAssumptionCache(F);
   auto &TLI = getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F);
   auto &TTI = getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
@@ -6329,8 +6333,9 @@ bool InstructionCombiningPass::runOnFunction(Function &F) {
           getAnalysisIfAvailable<BranchProbabilityInfoWrapperPass>())
     BPI = &WrapperPass->getBPI();
 
-  return combineInstructionsOverFunction(F, Worklist, AA, AC, TLI, TTI, DT, ORE,
-                                         BFI, BPI, PSI, InstCombineOptions());
+  return combineInstructionsOverFunction(F, Worklist, AA, AC, KBD, TLI, TTI, DT,
+                                         ORE, BFI, BPI, PSI,
+                                         InstCombineOptions());
 }
 
 char InstructionCombiningPass::ID = 0;
@@ -6340,6 +6345,7 @@ InstructionCombiningPass::InstructionCombiningPass() : FunctionPass(ID) {}
 INITIALIZE_PASS_BEGIN(InstructionCombiningPass, "instcombine",
                       "Combine redundant instructions", false, false)
 INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker)
+INITIALIZE_PASS_DEPENDENCY(KnownBitsDataflowAnalysisWrapperPass)
 INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
 INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass)
 INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
diff --git a/llvm/unittests/Analysis/CMakeLists.txt b/llvm/unittests/Analysis/CMakeLists.txt
index 9ca57024a6134..77a34a464153b 100644
--- a/llvm/unittests/Analysis/CMakeLists.txt
+++ b/llvm/unittests/Analysis/CMakeLists.txt
@@ -35,6 +35,7 @@ set(ANALYSIS_TEST_SOURCES
   InlineCostTest.cpp
   IR2VecTest.cpp
   IVDescriptorsTest.cpp
+  KnownBitsDataflowTest.cpp
   LastRunTrackingAnalysisTest.cpp
   LazyCallGraphTest.cpp
   LoadsTest.cpp
diff --git a/llvm/unittests/Analysis/KnownBitsDataflowTest.cpp b/llvm/unittests/Analysis/KnownBitsDataflowTest.cpp
new file mode 100644
index 0000000000000..b251d3b0173db
--- /dev/null
+++ b/llvm/unittests/Analysis/KnownBitsDataflowTest.cpp
@@ -0,0 +1,533 @@
+//===- KnownBitsDataflowTest.cpp ------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Analysis/KnownBitsDataflow.h"
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/SourceMgr.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+static Instruction *findInstructionByName(Function *F, StringRef Name) {
+  for (Instruction &I : instructions(F))
+    if (I.getName() == Name)
+      return &I;
+  return nullptr;
+}
+
+std::unique_ptr<Module> parseIR(LLVMContext &Ctx, StringRef Assembly) {
+  SMDiagnostic Err;
+  std::unique_ptr<Module> M = parseAssemblyString(Assembly, Err, Ctx);
+  if (!M)
+    Err.print(__FILE__, errs());
+  return M;
+}
+
+template <typename RangeT>
+static auto make_knownbits_range(RangeT &&R) { // NOLINT
+  return make_filter_range(R, [](const auto &V) {
+    return V->getType()->getScalarType()->isIntOrPtrTy();
+  });
+}
+
+/// Simply exposes some routines in KnownBitsDataflow, and adds a few for
+/// testing.
+struct DataflowForTest : public KnownBitsDataflow {
+private:
+  const Function &F;
+
+public:
+  DataflowForTest(const Function &F) : F(F) {}
+  SmallVector<KnownBitsVH, 16> computeRoots() const {
+    return KnownBitsDataflow::computeRoots(F);
+  }
+  bool contains(const Value *V) const { return KnownBitsDataflow::contains(V); }
+  KnownBits at(const Value *V) const {
+    auto It = find_as(V);
+    assert(It != end() && "Expected key in Map");
+    return It->second;
+  }
+  // Returns an unordered list.
+  auto computeLeaves() const {
+    auto IsLeaf = [](const Value *V) {
+      return make_knownbits_range(V->users()).empty();
+    };
+    return make_filter_range(keys(), [&IsLeaf](const KnownBitsVH &V) {
+      return IsLeaf(V.getValPtr());
+    });
+  }
+  void setKB(const Value *V, KnownBits Known) {
+    value_as(V) = {Known, nullptr};
+  }
+  void setAllZero(const Value *V) { value_as(V).setAllZero(); }
+  void setAllOnes(const Value *V) { value_as(V).setAllOnes(); }
+  void invalidate(const Value *V) {
+    return KnownBitsDataflow::invalidate(key_as(V));
+  }
+  bool isAllOnes(const Value *V) const { return at(V).isAllOnes(); }
+  void print(raw_ostream &OS) { KnownBitsDataflow::print(F, OS); }
+};
+
+/// Additionally initializes the map with the entire graph.
+struct DataflowInitializerForTest : public DataflowForTest {
+  DataflowInitializerForTest(Function &F) : DataflowForTest(F) {
+    initializeEntireGraph(F);
+  }
+  void initializeEntireGraph(const Function &F) {
+    // We emplace all-conflict that's different from unknown for testing. This
+    // is because unknown KnownBits are used to represent invalidated values.
+    auto GetAllConflict = [](unsigned BitWidth) {
+      KnownBits Known(BitWidth);
+      Known.setAllConflict();
+      return Known;
+    };
+    for (const Value *V : make_knownbits_range(make_pointer_range(F.args())))
+      emplace_as(V, GetAllConflict(KnownBitsDataflow::getBitWidth(
+                        V->getType(), F.getDataLayout())));
+
+    // Now collect all Instructions that aren't reachable from the function's
+    // arguments, updating Roots, as we test for unreachability.
+    for (const BasicBlock &BB : F) {
+      for (const Value *V : make_knownbits_range(make_pointer_range(BB))) {
+        emplace_as(V, GetAllConflict(KnownBitsDataflow::getBitWidth(
+                          V->getType(), F.getDataLayout())));
+      }
+    }
+  }
+};
+
+TEST(KnownBitsDataflow, BasicConstruction) {
+  LLVMContext Ctx;
+  std::unique_ptr<Module> M = parseIR(Ctx, R"(
+define void @test(i32 %n) {
+entry:
+  br label %loop
+loop:
+  %phi_counter = phi i32 [ 0, %entry ], [ %next_counter, %loop ]
+  %phi_result = phi i32 [ 1, %entry ], [ %result, %loop ]
+  %counter = add i32 %phi_counter, 1
+  %result = mul i32 %phi_result, 2
+  %next_counter = add i32 %counter, 1
+  %cond = icmp slt i32 %next_counter, %n
+  br i1 %cond, label %loop, label %exit
+exit:
+  store i32 %result, ptr poison
+  ret void
+})");
+  Function *F = M->getFunction("test");
+  DataflowInitializerForTest KBCache(*F);
+  Argument *ArgN = &*F->arg_begin();
+  Instruction *Counter = findInstructionByName(F, "counter");
+  Instruction *NextCounter = findInstructionByName(F, "next_counter");
+  Instruction *Result = findInstructionByName(F, "result");
+  Instruction *PhiCounter = findInstructionByName(F, "phi_counter");
+  Instruction *PhiResult = findInstructionByName(F, "phi_result");
+  Instruction *Cond = findInstructionByName(F, "cond");
+
+  EXPECT_TRUE(KBCache.contains(ArgN));
+  EXPECT_TRUE(KBCache.contains(PhiCounter));
+  EXPECT_TRUE(KBCache.contains(PhiResult));
+  EXPECT_TRUE(KBCache.contains(Counter));
+  EXPECT_TRUE(KBCache.contains(Result));
+  EXPECT_TRUE(KBCache.contains(NextCounter));
+  EXPECT_TRUE(KBCache.contains(Cond));
+  EXPECT_EQ(KBCache.size(), 7u);
+}
+
+TEST(KnownBitsDataflow, ConstructionWithIntAndPtr) {
+  LLVMContext Ctx;
+  std::unique_ptr<Module> M = parseIR(Ctx, R"(
+define void @test(i32 %int_arg, float %float_arg, ptr %ptr_arg, <2 x i32> %vec_int_arg, <2 x ptr> %vec_ptr_arg) {
+entry:
+  br i1 poison, label %then, label %else
+then:
+  %int_val = add i32 %int_arg, 1
+  %float_val = fadd float %float_arg, 1.0
+  %vec_val = add <2 x i32> %vec_int_arg, <i32 1, i32 2>
+  br label %merge
+else:
+  %fpconv = fptoui float %float_arg to i32
+  %int_val2 = mul i32 %int_arg, %fpconv
+  %ptr_val = getelementptr i8, ptr %ptr_arg, i32 4
+  %vec_val2 = mul <2 x i32> %vec_int_arg, <i32 3, i32 4>
+  br label %merge
+merge:
+  %phi_int = phi i32 [ %int_val, %then ], [ %int_val2, %else ]
+  %phi_float = phi float [ %float_val, %then ], [ %float_arg, %else ]
+  %phi_ptr = phi ptr [ %ptr_arg, %then ], [ %ptr_val, %else ]
+  %phi_vec = phi <2 x i32> [ %vec_val, %then ], [ %vec_val2, %else ]
+  %final_int = add i32 %phi_int, 5
+  %vec_ptr_conv = ptrtoint <2 x ptr> %vec_ptr_arg to <2 x i32>
+  %final_vec = add <2 x i32> %phi_vec, %vec_ptr_conv
+  store float %phi_float, ptr %phi_ptr
+  ret void
+})");
+  Function *F = M->getFunction("test");
+  DataflowInitializerForTest KBCache(*F);
+  auto *ArgIt = F->arg_begin();
+  Argument *IntArg = &*ArgIt++;
+  Argument *FloatArg = &*ArgIt++;
+  Argument *PtrArg = &*ArgIt++;
+  Argument *VecIntArg = &*ArgIt++;
+  Argument *VecPtrArg = &*ArgIt++;
+  Instruction *IntVal = findInstructionByName(F, "int_val");
+  Instruction *FloatVal = findInstructionByName(F, "float_val");
+  Instruction *VecVal = findInstructionByName(F, "vec_val");
+  Instruction *IntVal2 = findInstructionByName(F, "int_val2");
+  Instruction *PtrVal = findInstructionByName(F, "ptr_val");
+  Instruction *VecVal2 = findInstructionByName(F, "vec_val2");
+  Instruction *PhiInt = findInstructionByName(F, "phi_int");
+  Instruction *PhiFloat = findInstructionByName(F, "phi_float");
+  Instruction *PhiPtr = findInstructionByName(F, "phi_ptr");
+  Instruction *PhiVec = findInstructionByName(F, "phi_vec");
+  Instruction *FinalInt = findInstructionByName(F, "final_int");
+  Instruction *FPConv = findInstructionByName(F, "fpconv");
+  Instruction *VecPtrConv = findInstructionByName(F, "vec_ptr_conv");
+  Instruction *FinalVec = findInstructionByName(F, "final_vec");
+
+  EXPECT_THAT(
+      KBCache.computeRoots(),
+      ::testing::ElementsAre(IntArg, PtrArg, VecIntArg, VecPtrArg, FPConv));
+  EXPECT_THAT(KBCache.computeLeaves(),
+              ::testing::UnorderedElementsAre(FinalInt, PhiPtr, FinalVec));
+
+  EXPECT_TRUE(KBCache.contains(IntArg));
+  EXPECT_FALSE(KBCache.contains(FloatArg));
+  EXPECT_TRUE(KBCache.contains(PtrArg));
+  EXPECT_TRUE(KBCache.contains(VecIntArg));
+  EXPECT_TRUE(KBCache.contains(VecPtrArg));
+  EXPECT_TRUE(KBCache.contains(IntVal));
+  EXPECT_TRUE(KBCache.contains(IntVal2));
+  EXPECT_TRUE(KBCache.contains(PhiInt));
+  EXPECT_TRUE(KBCache.contains(FinalInt));
+  EXPECT_TRUE(KBCache.contains(VecVal));
+  EXPECT_TRUE(KBCache.contains(VecVal2));
+  EXPECT_TRUE(KBCache.contains(PhiVec));
+  EXPECT_TRUE(KBCache.contains(FPConv));
+  EXPECT_TRUE(KBCache.contains(VecPtrConv));
+  EXPECT_TRUE(KBCache.contains(FinalVec));
+  EXPECT_FALSE(KBCache.contains(FloatVal));
+  EXPECT_TRUE(KBCache.contains(PtrVal));
+  EXPECT_FALSE(KBCache.contains(PhiFloat));
+  EXPECT_TRUE(KBCache.contains(PhiPtr));
+
+  EXPECT_EQ(KBCache.size(), 16u);
+}
+
+TEST(KnownBitsDataflow, ConstructionWithNestedLoop) {
+  LLVMContext Ctx;
+  std::unique_ptr<Module> M = parseIR(Ctx, R"(
+define void @test(i32 %n, i32 %m) {
+entry:
+  br label %outer_loop
+outer_loop:
+  %outer_phi = phi i32 [ 0, %entry ], [ %outer_next, %outer_latch ]
+  br label %inner_loop
+inner_loop:
+  %inner_phi = phi i32 [ 0, %outer_loop ], [ %inner_next, %inner_loop ]
+  %inner_next = add i32 %inner_phi, 1
+  %inner_cond = icmp slt i32 %inner_next, %m
+  br i1 %inner_cond, label %inner_loop, label %outer_latch
+outer_latch:
+  %outer_next = add i32 %outer_phi, 1
+  %outer_cond = icmp slt i32 %outer_next, %n
+  br i1 %outer_cond, label %outer_loop, label %exit
+exit:
+  ret void
+})");
+  Function *F = M->getFunction("test");
+  DataflowInitializerForTest KBCache(*F);
+  auto *ArgIt = F->arg_begin();
+  Argument *ArgN = &*ArgIt++;
+  Argument *ArgM = &*ArgIt;
+  Instruction *OuterPHI = findInstructionByName(F, "outer_phi");
+  Instruction *InnerPHI = findInstructionByName(F, "inner_phi");
+  Instruction *InnerNext = findInstructionByName(F, "inner_next");
+  Instruction *OuterNext = findInstructionByName(F, "outer_next");
+  Instruction *InnerCond = findInstructionByName(F, "inner_cond");
+  Instruction *OuterCond = findInstructionByName(F, "outer_cond");
+
+  EXPECT_THAT(KBCache.computeRoots(),
+              ::testing::ElementsAre(ArgN, ArgM, OuterPHI, InnerPHI));
+  EXPECT_THAT(KBCache.computeLeaves(),
+              ::testing::UnorderedElementsAre(OuterCond, InnerCond));
+
+  EXPECT_TRUE(KBCache.contains(ArgN));
+  EXPECT_TRUE(KBCache.contains(ArgM));
+  EXPECT_TRUE(KBCache.contains(OuterPHI));
+  EXPECT_TRUE(KBCache.contains(InnerPHI));
+  EXPECT_TRUE(KBCache.contains(InnerNext));
+  EXPECT_TRUE(KBCache.contains(OuterNext));
+  EXPECT_TRUE(KBCache.contains(InnerCond));
+  EXPECT_TRUE(KBCache.contains(OuterCond));
+  EXPECT_EQ(KBCache.size(), 8u);
+}
+
+TEST(KnownBitsDataflow, ForwardDataflowBitsSingleBB) {
+  LLVMContext Ctx;
+  std::unique_ptr<Module> M = parseIR(Ctx, R"(
+define void @test(i32 %arg, <2 x i32> %vec_arg) {
+  %counter = add i32 %arg, 1
+  %result = mul i32 %counter, 2
+  %next_counter = add i32 %result, 3
+  %branch_val = sub i32 %next_counter, 1
+  %merge_val = add i32 %branch_val, 5
+  store i32 %merge_val, ptr poison
+  ret void
+})");
+  Function *F = M->getFunction("test");
+  DataflowInitializerForTest KBCache(*F);
+  Argument *Arg = &*F->arg_begin();
+  Argument *VecArg = &*F->arg_begin();
+  Instruction *Counter = findInstructionByName(F, "counter");
+  Instruction *NextCounter = findInstructionByName(F, "next_counter");
+  Instruction *Result = findInstructionByName(F, "result");
+  Instruction *BranchVal = findInstructionByName(F, "branch_val");
+  Instruction *MergeVal = findInstructionByName(F, "merge_val");
+  KnownBits Known32(32);
+  Known32.setAllOnes();
+
+  KBCache.setKB(Arg, Known32);
+  KBCache.setKB(Counter, Known32);
+  KBCache.setKB(Result, Known32);
+  KBCache.setKB(NextCounter, Known32);
+  KBCache.setKB(BranchVal, Known32);
+  KBCache.setKB(MergeVal, Known32);
+  KBCache.setAllOnes(VecArg);
+
+  EXPECT_TRUE(KBCache.isAllOnes(Arg));
+  EXPECT_TRUE(KBCache.isAllOnes(Counter));
+  EXPECT_TRUE(KBCache.isAllOnes(Result));
+  EXPECT_TRUE(KBCache.isAllOnes(NextCounter));
+  EXPECT_TRUE(KBCache.isAllOnes(BranchVal));
+  EXPECT_TRUE(KBCache.isAllOnes(MergeVal));
+  EXPECT_TRUE(KBCache.isAllOnes(VecArg));
+
+  KBCache.invalidate(Counter);
+
+  EXPECT_TRUE(KBCache.isAllOnes(Arg));
+  EXPECT_TRUE(KBCache.isAllOnes(VecArg));
+  EXPECT_FALSE(KBCache.lookup(Counter));
+  EXPECT_FALSE(KBCache.lookup(Result));
+  EXPECT_FALSE(KBCache.lookup(NextCounter));
+  EXPECT_FALSE(KBCache.lookup(BranchVal));
+  EXPECT_FALSE(KBCache.lookup(MergeVal));
+}
+
+TEST(KnownBitsDataflow, ForwardDataflowMultipleBBs) {
+  LLVMContext Ctx;
+  std::unique_ptr<Module> M = parseIR(Ctx, R"(
+define void @test(i32 %n, i1 %cond) {
+entry:
+  %counter = add i32 %n, 1
+  br i1 %cond, label %then, label %else
+then:
+  %branch_val = mul i32 %counter, 2
+  br label %merge
+else:
+  %result = add i32 %counter, 3
+  br label %merge
+merge:
+  %merge_val = phi i32 [ %branch_val, %then ], [ %result, %else ]
+  %next_counter = add i32 %merge_val, 1
+  store i32 %next_counter, ptr poison
+  ret void
+})");
+  Function *F = M->getFunction("test");
+  DataflowInitializerForTest KBCache(*F);
+  auto *ArgIt = F->arg_begin();
+  Argument *ArgN = &*ArgIt++;
+  Argument *ArgCond = &*ArgIt;
+  Instruction *Counter = findInstructionByName(F, "counter");
+  Instruction *NextCounter = findInstructionByName(F, "next_counter");
+  Instruction *Result = findInstructionByName(F, "result");
+  Instruction *BranchVal = findInstructionByName(F, "branch_val");
+  Instruction *MergeVal = findInstructionByName(F, "merge_val");
+  KnownBits Known32(32);
+  Known32.setAllOnes();
+
+  KBCache.setKB(ArgN, Known32);
+  KBCache.setKB(Counter, Known32);
+  KBCache.setKB(BranchVal, Known32);
+  KBCache.setKB(Result, Known32);
+  KBCache.setKB(MergeVal, Known32);
+  KBCache.setKB(NextCounter, Known32);
+  KBCache.setAllOnes(ArgCond);
+
+  EXPECT_TRUE(KBCache.isAllOnes(Counter));
+  EXPECT_TRUE(KBCache.isAllOnes(BranchVal));
+  EXPECT_TRUE(KBCache.isAllOnes(Result));
+  EXPECT_TRUE(KBCache.isAllOnes(MergeVal));
+  EXPECT_TRUE(KBCache.isAllOnes(NextCounter));
+
+  KBCache.invalidate(Result);
+
+  EXPECT_TRUE(KBCache.isAllOnes(ArgN));
+  EXPECT_TRUE(KBCache.isAllOnes(ArgCond));
+  EXPECT_TRUE(KBCache.isAllOnes(Counter));
+  EXPECT_TRUE(KBCache.isAllOnes(BranchVal));
+  EXPECT_FALSE(KBCache.lookup(Result));
+  EXPECT_FALSE(KBCache.lookup(MergeVal));
+  EXPECT_FALSE(KBCache.lookup(NextCounter));
+}
+
+TEST(KnownBitsDataflow, ForwardDataflowPartialInitialization) {
+  LLVMContext Ctx;
+  std::unique_ptr<Module> M = parseIR(Ctx, R"(
+define void @test(i32 %n, i1 %cond) {
+entry:
+  %counter = add i32 %n, 1
+  br i1 %cond, label %then, label %else
+then:
+  %branch_val = mul i32 %counter, 2
+  br label %merge
+else:
+  %result = add i32 %counter, 3
+  br label %merge
+merge:
+  %merge_val = phi i32 [ %branch_val, %then ], [ %result, %else ]
+  %next_counter = add i32 %merge_val, 1
+  store i32 %next_counter, ptr poison
+  ret void
+})");
+  Function *F = M->getFunction("test");
+  DataflowForTest KBCache(*F);
+  auto *ArgIt = F->arg_begin();
+  Argument *ArgN = &*ArgIt++;
+  Argument *ArgCond = &*ArgIt;
+  Instruction *Counter = findInstructionByName(F, "counter");
+  Instruction *NextCounter = findInstructionByName(F, "next_counter");
+  Instruction *Result = findInstructionByName(F, "result");
+  Instruction *BranchVal = findInstructionByName(F, "branch_val");
+  Instruction *MergeVal = findInstructionByName(F, "merge_val");
+  KnownBits Known32(32);
+  Known32.setAllOnes();
+
+  KBCache.emplace_as(ArgN, Known32);
+  KBCache.emplace_as(BranchVal, Known32);
+  KBCache.emplace_as(NextCounter, Known32);
+  EXPECT_EQ(KBCache.size(), 3u);
+
+  EXPECT_FALSE(KBCache.contains(ArgCond));
+  EXPECT_FALSE(KBCache.contains(Result));
+  EXPECT_FALSE(KBCache.contains(Counter));
+  EXPECT_FALSE(KBCache.contains(MergeVal));
+
+  EXPECT_TRUE(KBCache.isAllOnes(ArgN));
+  EXPECT_TRUE(KBCache.isAllOnes(BranchVal));
+  EXPECT_TRUE(KBCache.isAllOnes(NextCounter));
+
+  KBCache.invalidate(BranchVal);
+
+  EXPECT_FALSE(KBCache.lookup(BranchVal));
+  EXPECT_FALSE(KBCache.lookup(NextCounter));
+  EXPECT_EQ(KBCache.size(), 3u);
+
+  std::string ActualOutput;
+  raw_string_ostream OS(ActualOutput);
+  KBCache.print(OS);
+  std::string ExpectedOutput =
+      R"(^ i32 %n | 11111111111111111111111111111111
+$   %next_counter = add i32 %merge_val, 1 | ????????????????????????????????
+    %branch_val = mul i32 %counter, 2 | ????????????????????????????????
+)";
+  EXPECT_EQ(ActualOutput, ExpectedOutput);
+}
+
+TEST(KnownBitsDataflow, IRManipulation) {
+  LLVMContext Ctx;
+  std::unique_ptr<Module> M = parseIR(Ctx, R"(
+define void @test(i32 %int_arg, float %float_arg, ptr %ptr_arg, <2 x i32> %vec_int_arg, <2 x ptr> %vec_ptr_arg) {
+entry:
+  br i1 poison, label %then, label %else
+then:
+  %int_val = add i32 %int_arg, 1
+  %float_val = fadd float %float_arg, 1.0
+  %vec_val = add <2 x i32> %vec_int_arg, <i32 1, i32 2>
+  br label %merge
+else:
+  %fpconv = fptoui float %float_arg to i32
+  %int_val2 = mul i32 %int_arg, %fpconv
+  %ptr_val = getelementptr i8, ptr %ptr_arg, i32 4
+  %vec_val2 = mul <2 x i32> %vec_int_arg, <i32 3, i32 4>
+  br label %merge
+merge:
+  %phi_int = phi i32 [ %int_val, %then ], [ %int_val2, %else ]
+  %phi_float = phi float [ %float_val, %then ], [ %float_arg, %else ]
+  %phi_ptr = phi ptr [ %ptr_arg, %then ], [ %ptr_val, %else ]
+  %phi_vec = phi <2 x i32> [ %vec_val, %then ], [ %vec_val2, %else ]
+  %final_int = add i32 %phi_int, 5
+  %vec_ptr_conv = ptrtoint <2 x ptr> %vec_ptr_arg to <2 x i32>
+  %final_vec = add <2 x i32> %phi_vec, %vec_ptr_conv
+  %final_dead = mul <2 x i32> %final_vec, splat(i32 2)
+  store float %phi_float, ptr %phi_ptr
+  ret void
+})");
+  Function *F = M->getFunction("test");
+  DataflowInitializerForTest KBCache(*F);
+  Instruction *PhiInt = findInstructionByName(F, "phi_int");
+  Instruction *PhiPtr = findInstructionByName(F, "phi_ptr");
+  Instruction *FinalVec = findInstructionByName(F, "final_vec");
+  Instruction *FinalInt = findInstructionByName(F, "final_int");
+  Instruction *VecPtrConv = findInstructionByName(F, "vec_ptr_conv");
+  Instruction *FinalDead = findInstructionByName(F, "final_dead");
+
+  EXPECT_EQ(KBCache.size(), 17u);
+  EXPECT_THAT(KBCache.computeLeaves(),
+              ::testing::UnorderedElementsAre(FinalInt, PhiPtr, FinalDead));
+  FinalInt->eraseFromParent();
+  FinalDead->eraseFromParent();
+  EXPECT_EQ(KBCache.size(), 15u);
+  EXPECT_THAT(KBCache.computeLeaves(),
+              ::testing::UnorderedElementsAre(PhiPtr, PhiInt, FinalVec));
+  VecPtrConv->replaceAllUsesWith(PoisonValue::get(VecPtrConv->getType()));
+  EXPECT_EQ(KBCache.size(), 15u);
+}
+
+TEST(KnownBitsDataflow, Print) {
+  LLVMContext Ctx;
+  std::unique_ptr<Module> M = parseIR(Ctx, R"(
+define void @test(i32 %n) {
+entry:
+  br label %loop
+loop:
+  %phi_counter = phi i32 [ 0, %entry ], [ %next_counter, %loop ]
+  %counter = add i32 %phi_counter, 1
+  %result = mul i32 %counter, 2
+  %next_counter = add i32 %result, 1
+  %cond = icmp slt i32 %next_counter, %n
+  br i1 %cond, label %loop, label %exit
+exit:
+  ret void
+})");
+  Function *F = M->getFunction("test");
+  DataflowInitializerForTest KBCache(*F);
+  Instruction *Result = findInstructionByName(F, "result");
+  KBCache.setAllZero(Result);
+  std::string ActualOutput;
+  raw_string_ostream OS(ActualOutput);
+  KBCache.print(OS);
+  std::string ExpectedOutput =
+      R"(^ i32 %n | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+$   %cond = icmp slt i32 %next_counter, %n | !
+^   %phi_counter = phi i32 [ 0, %entry ], [ %next_counter, %loop ] | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+    %counter = add i32 %phi_counter, 1 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+    %result = mul i32 %counter, 2 | 00000000000000000000000000000000
+    %next_counter = add i32 %result, 1 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+)";
+  EXPECT_EQ(ActualOutput, ExpectedOutput);
+}
+} // namespace



More information about the llvm-commits mailing list