[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