[llvm] Reapply "[IR] Reject unhandled assume bundles and seperate them from normal attributes" (#202336) (PR #202355)

Nikolas Klauser via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 8 08:04:43 PDT 2026


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/202355

The crash is fixed by checking for equality of value and assumption
instead of asserting it.

Original message:
This patch introduces the concepts of bundle attributes and updates
the code base to use them in most places where assume bundles are handled.
The notable exception is the knowledge retention API, since it tries to
be generic for attributes. However, the `--assume-preserve-all` flag is
removed, since all attributes not listed in the bundle attributes are
rejected by the verifier now. This shouldn't be a huge problem, since any
attributes not listed there were completely unused. New functions with the
naming scheme `getAssume<AttrName>Info` are also added for attributes which
are used without having to enable experimental features.

This reverts commit d11a71905634e8fee7f4ab9c489317cccc23e0ad.


>From 4714cb2b8aedf26a02f6730c6d38904bd73d2b93 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 8 Jun 2026 16:44:03 +0200
Subject: [PATCH] Reapply "[IR] Reject unhandled assume bundles and seperate
 them from normal attributes" (#202336)

The crash is fixed by checking for equality of value and assumption
instead of asserting it.

Original message:
This patch introduces the concepts of bundle attributes and updates
the code base to use them in most places where assume bundles are handled.
The notable exception is the knowledge retention API, since it tries to
be generic for attributes. However, the `--assume-preserve-all` flag is
removed, since all attributes not listed in the bundle attributes are
rejected by the verifier now. This shouldn't be a huge problem, since any
attributes not listed there were completely unused. New functions with the
naming scheme `getAssume<AttrName>Info` are also added for attributes which
are used without having to enable experimental features.

This reverts commit d11a71905634e8fee7f4ab9c489317cccc23e0ad.
---
 llvm/docs/LangRef.rst                         | 110 ++++-----
 llvm/docs/ReleaseNotes.md                     |   5 +
 llvm/include/llvm/IR/BundleAttributes.def     |  22 ++
 llvm/include/llvm/IR/BundleAttributes.h       |  63 +++++
 llvm/include/llvm/IR/InstrTypes.h             |   6 +
 .../llvm/Transforms/Utils/PredicateInfo.h     |   5 +-
 llvm/lib/Analysis/LazyValueInfo.cpp           |  34 +--
 llvm/lib/Analysis/ValueTracking.cpp           |  57 +++--
 llvm/lib/IR/BundleAttributes.cpp              |  64 ++++++
 llvm/lib/IR/CMakeLists.txt                    |   1 +
 llvm/lib/IR/Verifier.cpp                      | 100 ++++----
 .../InstCombine/InstCombineCalls.cpp          |  89 +++----
 .../Transforms/Utils/AssumeBundleBuilder.cpp  |   9 +-
 llvm/lib/Transforms/Utils/PredicateInfo.cpp   |  17 +-
 .../lvi-after-jumpthreading.ll                | 189 ---------------
 .../Analysis/ValueTracking/assume-on-value.ll |  40 ++++
 .../ValueTracking/assume-queries-counter.ll   | 112 ---------
 llvm/test/Analysis/ValueTracking/assume.ll    |  69 ++++++
 .../AlignmentFromAssumptions/simple.ll        |   5 +-
 llvm/test/Transforms/Attributor/nofpclass.ll  |  28 ---
 llvm/test/Transforms/Attributor/nofree.ll     |  81 -------
 llvm/test/Transforms/InstCombine/assume.ll    |  71 ++++++
 llvm/test/Transforms/Util/assume-builder.ll   | 217 ------------------
 llvm/test/Verifier/assume-bundles.ll          |  81 ++++++-
 .../Analysis/AssumeBundleQueriesTest.cpp      |  27 ---
 25 files changed, 631 insertions(+), 871 deletions(-)
 create mode 100644 llvm/include/llvm/IR/BundleAttributes.def
 create mode 100644 llvm/include/llvm/IR/BundleAttributes.h
 create mode 100644 llvm/lib/IR/BundleAttributes.cpp
 delete mode 100644 llvm/test/Analysis/LazyValueAnalysis/lvi-after-jumpthreading.ll
 create mode 100644 llvm/test/Analysis/ValueTracking/assume-on-value.ll
 delete mode 100644 llvm/test/Analysis/ValueTracking/assume-queries-counter.ll

diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 5e5ebdf80773f..ca1f007865c03 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1587,6 +1587,8 @@ Currently, only the following parameter attributes are defined:
     :ref:`bitcast instruction <i_bitcast>`. This is not a valid attribute for
     return values and can only be applied to one parameter.
 
+.. _attr_nonnull:
+
 ``nonnull``
     This indicates that the parameter or return pointer is not null. This
     attribute may only be applied to pointer-typed parameters. This is not
@@ -1598,6 +1600,8 @@ Currently, only the following parameter attributes are defined:
     The ``nonnull`` attribute should be combined with the ``noundef`` attribute
     to ensure a pointer is not null or otherwise the behavior is undefined.
 
+.. _attr_dereferenceable:
+
 ``dereferenceable(<n>)``
     This indicates that the parameter or return pointer is dereferenceable. This
     attribute may only be applied to pointer-typed parameters. A pointer that
@@ -1610,9 +1614,9 @@ Currently, only the following parameter attributes are defined:
     ``null_pointer_is_valid`` function attribute is present.
     ``n`` should be a positive number. The pointer should be well defined,
     otherwise it is undefined behavior. This means ``dereferenceable(<n>)``
-    implies ``noundef``. When used in an assume operand bundle, more restricted
-    semantics apply. See  :ref:`assume operand bundles <assume_opbundles>` for
-    more details.
+    implies ``noundef``.
+
+.. _attr_dereferenceable_or_null:
 
 ``dereferenceable_or_null(<n>)``
     This indicates that the parameter or return value isn't both
@@ -1667,6 +1671,8 @@ Currently, only the following parameter attributes are defined:
     only valid on intrinsic declarations and cannot be applied to a
     call site or arbitrary function.
 
+.. _attr_noundef:
+
 ``noundef``
     This attribute applies to parameters and return values. If the value
     representation contains any undefined or poison bits, the behavior is
@@ -2149,6 +2155,9 @@ For example:
     uses the ``nobuiltin`` attribute. This is only valid at call sites for
     direct calls to functions that are declared with the ``nobuiltin``
     attribute.
+
+.. _attr_cold:
+
 ``cold``
     This attribute indicates that this function is rarely called. When
     computing edge weights, basic blocks post-dominated by a cold
@@ -3127,33 +3136,57 @@ Assume Operand Bundles
 ^^^^^^^^^^^^^^^^^^^^^^
 
 Operand bundles on an :ref:`llvm.assume <int_assume>` allow representing
-assumptions, such as that a :ref:`parameter attribute <paramattrs>` or a
-:ref:`function attribute <fnattrs>` holds for a certain value at a certain
-location. Operand bundles enable assumptions that are either hard or impossible
-to represent as a boolean argument of an :ref:`llvm.assume <int_assume>`.
+assumptions that hold at the location of the assume. Operand bundles enable
+assumptions that are either hard or impossible to represent as a boolean
+argument of an :ref:`llvm.assume <int_assume>`.
 
 Assumes with operand bundles must have ``i1 true`` as the condition operand.
 
-An assume operand bundle has the form:
+Just like for the argument of :ref:`llvm.assume <int_assume>`, if any of the
+provided guarantees are violated at runtime the behavior is undefined.
 
-::
+While attributes expect constant arguments, assume operand bundles may be
+provided a dynamic value, for example:
 
-      "<tag>"([ <arguments>] ])
+.. code-block:: llvm
 
-In the case of function or parameter attributes, the operand bundle has the
-restricted form:
+      call void @llvm.assume(i1 true) ["align"(ptr %val, i32 %align)]
 
-::
+The following attributes are currently accepted:
+
+``"align"(ptr %p, i64 %align)``, ``"align"(ptr %p, i64 %align, i64 %offset)``
+  Equivalent to :ref:`align(%align) <attr_align>` on ``%p``, or ``%p - %offset``
+  if the ``%offset`` argument exists, except that ``%align`` may be a
+  non-power-of-two alignment (including a zero alignment). If ``%align`` is not
+  a power of two the pointer value must be all-zero. Otherwise the behavior is
+  undefined.
+
+``"cold"()``
+  Equivalent to :ref:`cold <attr_cold>`.
+
+``"dereferenceable"(ptr %p, i64 %size)``
+  Equivalent to :ref:`dereferenceable(%size) <attr_dereferenceable>` on ``%p``,
+  except that ``%size`` may also be zero, in which case the bundle doesn't
+  imply ``nonnull``.
+
+``"dereferenceable_or_null"(ptr %p, i64 %size)``
+  Equivalent to :ref:`dereferenceable_or_null(%size)
+  <attr_dereferenceable_or_null>` on ``%p``, except that ``%size`` may also be
+  zero.
+
+``"ignore"(...)``
+  Doesn't imply anything and is ignored. This is used to drop an assume where
+  the ``llvm.assume`` call cannot be replaced or dropped.
 
-      "<tag>"([ <holds for value> [, <attribute argument>] ])
+``"nonnull"(ptr %p)``
+  Equivalent to :ref:`nonnull <attr_nonnull>` on ``%p``.
 
-* The tag of the operand bundle is usually the name of the attribute that can be
-  assumed to hold. It can also be `ignore`; this tag doesn't contain any
-  information and should be ignored.
-* The first argument, if present, is the value for which the attribute holds.
-* The second argument, if present, is an argument of the attribute.
+``"noundef"(any_type %v)``
+  Equivalent to :ref:`noundef <attr_noundef>` on ``%v``.
 
-If there are no arguments the attribute is a property of the call location.
+``"separate_storage"(ptr %p1, ptr %p2)``
+  This indicates that no pointer :ref:`based <pointeraliasing>` on one of its
+  arguments can alias any pointer based on the other.
 
 For example:
 
@@ -3171,39 +3204,6 @@ allows the optimizer to assume that at location of call to
 allows the optimizer to assume that the :ref:`llvm.assume <int_assume>`
 call location is cold and that ``%val`` may not be null.
 
-Just like for the argument of :ref:`llvm.assume <int_assume>`, if any of the
-provided guarantees are violated at runtime the behavior is undefined.
-
-While attributes expect constant arguments, assume operand bundles may be
-provided a dynamic value, for example:
-
-.. code-block:: llvm
-
-      call void @llvm.assume(i1 true) ["align"(ptr %val, i32 %align)]
-
-If the operand bundle value violates any requirements on the attribute value,
-the behavior is undefined, unless one of the following exceptions applies:
-
-* ``"align"`` operand bundles may specify a non-power-of-two alignment
-  (including a zero alignment). If this is the case, then the pointer value
-  must be an all-zero pointer, otherwise the behavior is undefined.
-
-* ``dereferenceable(<n>)`` operand bundles only guarantee the pointer is
-  dereferenceable at the point of the assumption. The pointer may not be
-  dereferenceable at later pointers, e.g., because it could have been freed.
-  Only ``n > 0`` implies that the pointer is dereferenceable.
-
-In addition to allowing operand bundles encoding function and parameter
-attributes, an assume operand bundle may also encode a ``separate_storage``
-operand bundle. This has the form:
-
-.. code-block:: llvm
-
-    separate_storage(<val1>, <val2>)``
-
-This indicates that no pointer :ref:`based <pointeraliasing>` on one of its
-arguments can alias any pointer based on the other.
-
 Even if the assumed property can be encoded as a boolean value, like
 ``nonnull``, using operand bundles to express the property can still have
 benefits:
@@ -8143,7 +8143,7 @@ Together these two attributes provide a four-way classification:
 
 - ``body`` only: main vectorized loop body
 - ``epilogue`` only: scalar epilogue loop after vectorization
-- Both ``body`` and ``epilogue``: vectorized epilogue 
+- Both ``body`` and ``epilogue``: vectorized epilogue
   (a remainder loop that was itself vectorized during epilogue
   vectorization)
 - Neither: a plain loop not produced by the vectorizer
@@ -8772,7 +8772,7 @@ is executed, followed by ``uint64_t`` value and execution count pairs.
 The value profiling kind is 0 for indirect call targets and 1 for memory
 operations. For indirect call targets, each profile value is a hash
 of the callee function name, and for memory operations each value is the
-byte length. It is illegal to have duplicate profile values (e.g., 
+byte length. It is illegal to have duplicate profile values (e.g.,
 duplicate function hashes for indirect calls or byte lengths for memory
 operations).
 
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index f234e9b5d6433..3b862814bdbad 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -87,6 +87,11 @@ Makes programs 10x faster by doing Special New Thing.
   and `ccc` agree for `void(ptr)` (x86_64, AArch64, RISC-V, ...) but is an ABI
   break on i686, MIPS O32, PowerPC64 ELFv1, and Lanai.
 
+* Assume bundles now only accept attributes that are actually handled.
+  Specifically, they are ``align``, ``cold``, ``dereferenceable``,
+  ``dereferenceable_or_null``, ``nonnull``, ``noundef`` and
+  ``separate_storage``.
+
 * Fast math flags are now permitted on `uitofp` and `sitofp`.
 
 ### Changes to LLVM infrastructure
diff --git a/llvm/include/llvm/IR/BundleAttributes.def b/llvm/include/llvm/IR/BundleAttributes.def
new file mode 100644
index 0000000000000..78c692393fb44
--- /dev/null
+++ b/llvm/include/llvm/IR/BundleAttributes.def
@@ -0,0 +1,22 @@
+//===- llvm/BundleAttributes.def - LLVM Bundle Attributes -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+ATTR(Align, align)
+ATTR(Cold, cold)
+ATTR(Dereferenceable, dereferenceable)
+ATTR(DereferenceableOrNull, dereferenceable_or_null)
+ATTR(Ignore, ignore)
+ATTR(NonNull, nonnull)
+
+// FIXME: Is this actually useful?
+// It's currently exclusively emitted through tests.
+ATTR(NoUndef, noundef)
+
+ATTR(SeparateStorage, separate_storage)
+
+#undef ATTR
diff --git a/llvm/include/llvm/IR/BundleAttributes.h b/llvm/include/llvm/IR/BundleAttributes.h
new file mode 100644
index 0000000000000..ee6e5b4bc1ef6
--- /dev/null
+++ b/llvm/include/llvm/IR/BundleAttributes.h
@@ -0,0 +1,63 @@
+//===- llvm/BundleAttributes.h - LLVM Bundle Attributes ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_IR_BUNDLE_ATTRIBUTES_H
+#define LLVM_IR_BUNDLE_ATTRIBUTES_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/InstrTypes.h"
+
+enum class BundleAttr {
+  None,
+#define ATTR(Name, String) Name,
+#include "BundleAttributes.def"
+};
+
+namespace llvm {
+
+LLVM_ABI StringRef getNameFromBundleAttr(BundleAttr);
+LLVM_ABI BundleAttr getBundleAttrFromString(StringRef);
+
+inline BundleAttr getBundleAttrFromOBU(OperandBundleUse OBU) {
+  return getBundleAttrFromString(OBU.getTagName());
+}
+
+struct AssumeAlignInfo {
+  const Use &Ptr;
+  const Use &Alignment;
+  std::optional<uint64_t> AlignmentVal;
+  std::optional<uint64_t> OffsetVal;
+};
+
+LLVM_ABI AssumeAlignInfo getAssumeAlignInfo(OperandBundleUse);
+
+struct AssumeDereferenceableInfo {
+  const Use &Ptr;
+  const Use &Count;
+};
+
+LLVM_ABI
+AssumeDereferenceableInfo getAssumeDereferenceableInfo(OperandBundleUse);
+
+struct AssumeNonNullInfo {
+  const Use &Ptr;
+};
+
+LLVM_ABI AssumeNonNullInfo getAssumeNonNullInfo(OperandBundleUse);
+
+struct AssumeSeparateStorageInfo {
+  const Use &Ptr1;
+  const Use &Ptr2;
+};
+
+LLVM_ABI
+AssumeSeparateStorageInfo getAssumeSeparateStorageInfo(OperandBundleUse);
+
+} // namespace llvm
+
+#endif // LLVM_IR_BUNDLE_ATTRIBUTES_H
diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h
index b575b32a244df..5f7df6a4eb6f8 100644
--- a/llvm/include/llvm/IR/InstrTypes.h
+++ b/llvm/include/llvm/IR/InstrTypes.h
@@ -2353,6 +2353,12 @@ class CallBase : public Instruction {
     return make_range(bundle_op_info_begin(), bundle_op_info_end());
   }
 
+  auto operand_bundles() const {
+    return map_range(bundle_op_infos(), [this](BundleOpInfo BOI) {
+      return operandBundleFromBundleOpInfo(BOI);
+    });
+  }
+
   /// Populate the BundleOpInfo instances and the Use& vector from \p
   /// Bundles.  Return the op_iterator pointing to the Use& one past the last
   /// last bundle operand use.
diff --git a/llvm/include/llvm/Transforms/Utils/PredicateInfo.h b/llvm/include/llvm/Transforms/Utils/PredicateInfo.h
index e472656d7b9fb..06ce720bda6a9 100644
--- a/llvm/include/llvm/Transforms/Utils/PredicateInfo.h
+++ b/llvm/include/llvm/Transforms/Utils/PredicateInfo.h
@@ -52,6 +52,7 @@
 
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallSet.h"
+#include "llvm/IR/BundleAttributes.h"
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/PassManager.h"
 #include "llvm/IR/ValueHandle.h"
@@ -132,9 +133,9 @@ class PredicateAssume : public PredicateBase {
 
 class PredicateBundleAssume : public PredicateAssume {
 public:
-  Attribute::AttrKind AttrKind;
+  BundleAttr AttrKind;
   PredicateBundleAssume(Value *Op, IntrinsicInst *AssumeInst,
-                        Attribute::AttrKind AttrKind)
+                        BundleAttr AttrKind)
       : PredicateAssume(PT_BundleAssume, Op, AssumeInst, nullptr),
         AttrKind(AttrKind) {}
 
diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp
index e78a47b8de68d..7a56a278c5e1e 100644
--- a/llvm/lib/Analysis/LazyValueInfo.cpp
+++ b/llvm/lib/Analysis/LazyValueInfo.cpp
@@ -14,7 +14,6 @@
 #include "llvm/Analysis/LazyValueInfo.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/STLExtras.h"
-#include "llvm/Analysis/AssumeBundleQueries.h"
 #include "llvm/Analysis/AssumptionCache.h"
 #include "llvm/Analysis/ConstantFolding.h"
 #include "llvm/Analysis/InstructionSimplify.h"
@@ -23,6 +22,7 @@
 #include "llvm/Analysis/ValueLattice.h"
 #include "llvm/Analysis/ValueTracking.h"
 #include "llvm/IR/AssemblyAnnotationWriter.h"
+#include "llvm/IR/BundleAttributes.h"
 #include "llvm/IR/CFG.h"
 #include "llvm/IR/ConstantRange.h"
 #include "llvm/IR/Constants.h"
@@ -856,26 +856,26 @@ void LazyValueInfoImpl::intersectAssumeOrGuardBlockValueConstantRange(
       continue;
 
     if (AssumeVH.Index != AssumptionCache::ExprResultIdx) {
-      if (RetainedKnowledge RK = getKnowledgeFromBundle(
-              *I, I->bundle_op_info_begin()[AssumeVH.Index])) {
-        if (RK.WasOn != Val)
-          continue;
-        switch (RK.AttrKind) {
-        case Attribute::NonNull:
-          BBLV = BBLV.intersect(ValueLatticeElement::getNot(
-              Constant::getNullValue(RK.WasOn->getType())));
+      auto OBU = I->getOperandBundleAt(AssumeVH.Index);
+      switch (getBundleAttrFromOBU(OBU)) {
+      case BundleAttr::NonNull:
+        if (getAssumeNonNullInfo(OBU).Ptr != Val)
           break;
+        BBLV = BBLV.intersect(ValueLatticeElement::getNot(
+            Constant::getNullValue(Val->getType())));
+        break;
 
-        case Attribute::Dereferenceable:
-          if (auto *CI = dyn_cast<ConstantInt>(RK.IRArgValue);
-              CI && !CI->isZero())
-            BBLV = BBLV.intersect(ValueLatticeElement::getNot(
-                Constant::getNullValue(RK.WasOn->getType())));
+      case BundleAttr::Dereferenceable: {
+        auto [Ptr, Count] = getAssumeDereferenceableInfo(OBU);
+        if (Ptr != Val)
           break;
+        if (auto *CI = dyn_cast<ConstantInt>(Count); CI && !CI->isZero())
+          BBLV = BBLV.intersect(ValueLatticeElement::getNot(
+              Constant::getNullValue(Val->getType())));
+      } break;
 
-        default:
-          break;
-        }
+      default:
+        break;
       }
     } else {
       BBLV = BBLV.intersect(*getValueFromCondition(Val, I->getArgOperand(0),
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 1261664c5b986..98f12b0a623be 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -38,6 +38,7 @@
 #include "llvm/IR/Argument.h"
 #include "llvm/IR/Attributes.h"
 #include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/BundleAttributes.h"
 #include "llvm/IR/Constant.h"
 #include "llvm/IR/ConstantFPRange.h"
 #include "llvm/IR/ConstantRange.h"
@@ -827,32 +828,29 @@ static bool isKnownNonZeroFromAssume(const Value *V, const SimplifyQuery &Q) {
            "Got assumption for the wrong function!");
 
     if (Elem.Index != AssumptionCache::ExprResultIdx) {
-      if (!V->getType()->isPointerTy())
-        continue;
-      if (RetainedKnowledge RK = getKnowledgeFromBundle(
-              *I, I->bundle_op_info_begin()[Elem.Index])) {
-        if (RK.WasOn != V)
-          continue;
-        bool AssumeImpliesNonNull = [&]() {
-          if (RK.AttrKind == Attribute::NonNull)
-            return true;
+      bool AssumeImpliesNonNull = [&]() {
+        auto OBU = I->getOperandBundleAt(Elem.Index);
+        switch (getBundleAttrFromOBU(OBU)) {
+        case BundleAttr::Dereferenceable: {
+          auto [Ptr, Count] = getAssumeDereferenceableInfo(OBU);
+          if (Ptr != V ||
+              NullPointerIsDefined(Q.CxtI->getFunction(),
+                                   V->getType()->getPointerAddressSpace()))
+            return false;
 
-          if (RK.AttrKind == Attribute::Dereferenceable) {
-            if (NullPointerIsDefined(Q.CxtI->getFunction(),
-                                     V->getType()->getPointerAddressSpace()))
-              return false;
-            assert(RK.IRArgValue &&
-                   "Dereferenceable attribute without IR argument?");
+          auto *CI = dyn_cast<ConstantInt>(Count);
+          return CI && !CI->isZero();
+        }
 
-            auto *CI = dyn_cast<ConstantInt>(RK.IRArgValue);
-            return CI && !CI->isZero();
-          }
+        case BundleAttr::NonNull:
+          return getAssumeNonNullInfo(OBU).Ptr == V;
 
+        default:
           return false;
-        }();
-        if (AssumeImpliesNonNull && isValidAssumeForContext(I, Q))
-          return true;
-      }
+        }
+      }();
+      if (AssumeImpliesNonNull && isValidAssumeForContext(I, Q))
+        return true;
       continue;
     }
 
@@ -1088,13 +1086,14 @@ void llvm::computeKnownBitsFromContext(const Value *V, KnownBits &Known,
            "Got assumption for the wrong function!");
 
     if (Elem.Index != AssumptionCache::ExprResultIdx) {
-      if (!V->getType()->isPointerTy())
-        continue;
-      if (RetainedKnowledge RK = getKnowledgeFromBundle(
-              *I, I->bundle_op_info_begin()[Elem.Index])) {
-        if (RK.WasOn == V && RK.AttrKind == Attribute::Alignment &&
-            isPowerOf2_64(RK.ArgValue) && isValidAssumeForContext(I, Q))
-          Known.Zero.setLowBits(Log2_64(RK.ArgValue));
+      if (auto OBU = I->getOperandBundleAt(Elem.Index);
+          getBundleAttrFromOBU(OBU) == BundleAttr::Align) {
+        auto [Ptr, _, Alignment, Offset] = getAssumeAlignInfo(OBU);
+        if (Ptr != V || !Alignment || !Offset || !isPowerOf2_64(*Alignment))
+          continue;
+        auto AlignVal = MinAlign(*Offset, *Alignment);
+        if (isValidAssumeForContext(I, Q))
+          Known.Zero.setLowBits(Log2_64(AlignVal));
       }
       continue;
     }
diff --git a/llvm/lib/IR/BundleAttributes.cpp b/llvm/lib/IR/BundleAttributes.cpp
new file mode 100644
index 0000000000000..3e4e65e8fd3d1
--- /dev/null
+++ b/llvm/lib/IR/BundleAttributes.cpp
@@ -0,0 +1,64 @@
+//===- llvm/BundleAttributes.cpp - LLVM Bundle Attributes -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/IR/BundleAttributes.h"
+
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/IR/Constants.h"
+
+using namespace llvm;
+
+StringRef llvm::getNameFromBundleAttr(BundleAttr BA) {
+  switch (BA) {
+#define ATTR(Name, Str)                                                        \
+  case BundleAttr::Name:                                                       \
+    return #Str;
+#include "llvm/IR/BundleAttributes.def"
+  case BundleAttr::None:
+    return "none";
+  }
+}
+
+BundleAttr llvm::getBundleAttrFromString(StringRef Str) {
+  return StringSwitch<BundleAttr>(Str)
+#define ATTR(Name, Str) .Case(#Str, BundleAttr::Name)
+#include "llvm/IR/BundleAttributes.def"
+      .Default(BundleAttr::None);
+}
+
+AssumeAlignInfo llvm::getAssumeAlignInfo(OperandBundleUse OBU) {
+  assert(OBU.getTagName() == "align" && OBU.Inputs.size() >= 2 &&
+         OBU.Inputs.size() <= 3);
+  AssumeAlignInfo Ret{OBU.Inputs[0], OBU.Inputs[1], std::nullopt, std::nullopt};
+  if (auto *Align = dyn_cast<ConstantInt>(OBU.Inputs[1]))
+    Ret.AlignmentVal = Align->getZExtValue();
+  if (OBU.Inputs.size() == 3) {
+    if (auto *Offset = dyn_cast<ConstantInt>(OBU.Inputs[2]))
+      Ret.OffsetVal = Offset->getZExtValue();
+  } else {
+    Ret.OffsetVal = 0;
+  }
+  return Ret;
+}
+
+AssumeSeparateStorageInfo
+llvm::getAssumeSeparateStorageInfo(OperandBundleUse OBU) {
+  assert(OBU.getTagName() == "separate_storage" && OBU.Inputs.size() == 2);
+  return {OBU.Inputs[0], OBU.Inputs[1]};
+}
+
+AssumeNonNullInfo llvm::getAssumeNonNullInfo(OperandBundleUse OBU) {
+  assert(OBU.getTagName() == "nonnull" && OBU.Inputs.size() == 1);
+  return {OBU.Inputs[0]};
+}
+
+AssumeDereferenceableInfo
+llvm::getAssumeDereferenceableInfo(OperandBundleUse OBU) {
+  assert(OBU.getTagName() == "dereferenceable" && OBU.Inputs.size() == 2);
+  return {OBU.Inputs[0], OBU.Inputs[1]};
+}
diff --git a/llvm/lib/IR/CMakeLists.txt b/llvm/lib/IR/CMakeLists.txt
index 5511c3b33b0d7..9cc45ef0e1773 100644
--- a/llvm/lib/IR/CMakeLists.txt
+++ b/llvm/lib/IR/CMakeLists.txt
@@ -6,6 +6,7 @@ add_llvm_component_library(LLVMCore
   AutoUpgrade.cpp
   BasicBlock.cpp
   BuiltinGCs.cpp
+  BundleAttributes.cpp
   Comdat.cpp
   ConstantFold.cpp
   ConstantFPRange.cpp
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 344314e39c38a..cb28ff5c633f3 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -64,6 +64,7 @@
 #include "llvm/IR/AttributeMask.h"
 #include "llvm/IR/Attributes.h"
 #include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/BundleAttributes.h"
 #include "llvm/IR/CFG.h"
 #include "llvm/IR/CallingConv.h"
 #include "llvm/IR/Comdat.h"
@@ -6077,56 +6078,67 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
       Check(Cond && Cond->isOne(),
             "assume with operand bundles must have i1 true condition", Call);
     }
-    for (auto &Elem : Call.bundle_op_infos()) {
-      unsigned ArgCount = Elem.End - Elem.Begin;
+    for (auto OBU : Call.operand_bundles()) {
       // Separate storage assumptions are special insofar as they're the only
       // operand bundles allowed on assumes that aren't parameter attributes.
-      if (Elem.Tag->getKey() == "separate_storage") {
-        Check(ArgCount == 2,
-              "separate_storage assumptions should have 2 arguments", Call);
-        Check(Call.getOperand(Elem.Begin)->getType()->isPointerTy() &&
-                  Call.getOperand(Elem.Begin + 1)->getType()->isPointerTy(),
-              "arguments to separate_storage assumptions should be pointers",
-              Call);
-        continue;
-      }
-      Check(Elem.Tag->getKey() == "ignore" ||
-                Attribute::isExistingAttribute(Elem.Tag->getKey()),
-            "tags must be valid attribute names", Call);
-      Attribute::AttrKind Kind =
-          Attribute::getAttrKindFromName(Elem.Tag->getKey());
-      if (Kind == Attribute::Alignment) {
-        Check(ArgCount <= 3 && ArgCount >= 2,
+
+      auto GetTypeAt = [&](unsigned Index) {
+        return OBU.Inputs[Index]->getType();
+      };
+
+      switch (getBundleAttrFromOBU(OBU)) {
+      case BundleAttr::None:
+        CheckFailed("tags must be valid attribute names", Call);
+        break;
+      case BundleAttr::Align:
+        Check(OBU.Inputs.size() >= 2 && OBU.Inputs.size() <= 3,
               "alignment assumptions should have 2 or 3 arguments", Call);
-        Check(Call.getOperand(Elem.Begin)->getType()->isPointerTy(),
-              "first argument should be a pointer", Call);
-        Check(Call.getOperand(Elem.Begin + 1)->getType()->isIntegerTy(),
-              "second argument should be an integer", Call);
-        if (ArgCount == 3)
-          Check(Call.getOperand(Elem.Begin + 2)->getType()->isIntegerTy(),
-                "third argument should be an integer if present", Call);
-        continue;
-      }
-      if (Kind == Attribute::Dereferenceable) {
-        Check(ArgCount == 2,
+        Check(GetTypeAt(0)->isPointerTy(), "first argument should be a pointer",
+              Call);
+        Check(GetTypeAt(1)->isIntegerTy() &&
+                  GetTypeAt(1)->getIntegerBitWidth() <= 64,
+              "second argument should be an integer with a maximum width of 64 "
+              "bits",
+              Call);
+        Check(OBU.Inputs.size() < 3 ||
+                  GetTypeAt(2)->isIntegerTy() &&
+                      GetTypeAt(2)->getIntegerBitWidth() <= 64,
+              "third argument should be an integer with a maximum width of 64 "
+              "bits if present",
+              Call);
+        break;
+      case BundleAttr::Cold:
+        Check(OBU.Inputs.size() == 0,
+              "cold assumptions should have no arguments", Call);
+        break;
+      case BundleAttr::Dereferenceable:
+      case BundleAttr::DereferenceableOrNull:
+        Check(OBU.Inputs.size() == 2,
               "dereferenceable assumptions should have 2 arguments", Call);
-        Check(Call.getOperand(Elem.Begin)->getType()->isPointerTy(),
-              "first argument should be a pointer", Call);
-        Check(Call.getOperand(Elem.Begin + 1)->getType()->isIntegerTy(),
+        Check(GetTypeAt(0)->isPointerTy(), "first argument should be a pointer",
+              Call);
+        Check(GetTypeAt(1)->isIntegerTy(),
               "second argument should be an integer", Call);
-        continue;
-      }
-      Check(ArgCount <= 2, "too many arguments", Call);
-      if (Kind == Attribute::None)
         break;
-      if (Attribute::isIntAttrKind(Kind)) {
-        Check(ArgCount == 2, "this attribute should have 2 arguments", Call);
-        Check(isa<ConstantInt>(Call.getOperand(Elem.Begin + 1)),
-              "the second argument should be a constant integral value", Call);
-      } else if (Attribute::canUseAsParamAttr(Kind)) {
-        Check((ArgCount) == 1, "this attribute should have one argument", Call);
-      } else if (Attribute::canUseAsFnAttr(Kind)) {
-        Check((ArgCount) == 0, "this attribute has no argument", Call);
+      case BundleAttr::Ignore:
+        break;
+      case BundleAttr::NonNull:
+        Check(OBU.Inputs.size() == 1,
+              "nonnull assumptions should have 1 argument", Call);
+        Check(GetTypeAt(0)->isPointerTy(), "first argument should be a pointer",
+              Call);
+        break;
+      case BundleAttr::NoUndef:
+        Check(OBU.Inputs.size() == 1,
+              "noundef assumptions should have 1 argument", Call);
+        break;
+      case BundleAttr::SeparateStorage:
+        Check(OBU.Inputs.size() == 2,
+              "separate_storage assumptions should have 2 arguments", Call);
+        Check(GetTypeAt(0)->isPointerTy() && GetTypeAt(1)->isPointerTy(),
+              "arguments to separate_storage assumptions should be pointers",
+              Call);
+        break;
       }
     }
     break;
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 7bcaa930511ee..c7c153df27f62 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -31,6 +31,7 @@
 #include "llvm/IR/AttributeMask.h"
 #include "llvm/IR/Attributes.h"
 #include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/BundleAttributes.h"
 #include "llvm/IR/Constant.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DataLayout.h"
@@ -3647,66 +3648,45 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
       return eraseInstFromFunction(*II);
     }
 
-    for (unsigned Idx = 0; Idx < II->getNumOperandBundles(); Idx++) {
-      OperandBundleUse OBU = II->getOperandBundleAt(Idx);
+    for (auto [Idx, OBU] : llvm::enumerate(II->operand_bundles())) {
+      switch (getBundleAttrFromOBU(OBU)) {
+      case BundleAttr::None:
+        llvm_unreachable("Unexpected Attribute");
+      case BundleAttr::Align: {
+        // Try to remove redundant alignment assumptions.
+        auto [Ptr, _, Alignment, Offset] = getAssumeAlignInfo(OBU);
 
-      // Separate storage assumptions apply to the underlying allocations, not
-      // any particular pointer within them. When evaluating the hints for AA
-      // purposes we getUnderlyingObject them; by precomputing the answers here
-      // we can avoid having to do so repeatedly there.
-      if (OBU.getTagName() == "separate_storage") {
-        assert(OBU.Inputs.size() == 2);
-        auto MaybeSimplifyHint = [&](const Use &U) {
-          Value *Hint = U.get();
-          // Not having a limit is safe because InstCombine removes unreachable
-          // code.
-          Value *UnderlyingObject = getUnderlyingObject(Hint, /*MaxLookup*/ 0);
-          if (Hint != UnderlyingObject)
-            replaceUse(const_cast<Use &>(U), UnderlyingObject);
-        };
-        MaybeSimplifyHint(OBU.Inputs[0]);
-        MaybeSimplifyHint(OBU.Inputs[1]);
-      }
-
-      // Try to remove redundant alignment assumptions.
-      if (OBU.getTagName() == "align" && OBU.Inputs.size() == 2) {
-        RetainedKnowledge RK = getKnowledgeFromOperandInAssume(
-            *cast<AssumeInst>(II), II->arg_size() + Idx);
-        if (!RK || RK.AttrKind != Attribute::Alignment ||
-            !isPowerOf2_64(RK.ArgValue) || !isa<ConstantInt>(RK.IRArgValue))
-          continue;
+        if (!Alignment || !Offset || *Offset != 0 || !isPowerOf2_64(*Alignment))
+          break;
 
         // Remove align 1 bundles; they don't add any useful information.
-        if (RK.ArgValue == 1)
+        if (*Alignment == 1)
           return CallBase::removeOperandBundleAt(II, Idx);
 
         // Don't try to remove align assumptions for pointers derived from
         // arguments. We might lose information if the function gets inline and
         // the align argument attribute disappears.
-        Value *UO = getUnderlyingObject(RK.WasOn);
+        Value *UO = getUnderlyingObject(Ptr);
         if (!UO || isa<Argument>(UO))
-          continue;
+          break;
 
         // Compute known bits for the pointer and drop the assume if the
         // known alignment isn't increased by it.
-        if (computeKnownBits(RK.WasOn, II).countMinTrailingZeros() <
-            Log2_64(RK.ArgValue))
+        if (computeKnownBits(Ptr, II).countMinTrailingZeros() <
+            Log2_64(*Alignment))
           continue;
         return CallBase::removeOperandBundleAt(II, Idx);
       }
 
-      if (OBU.getTagName() == "nonnull" && OBU.Inputs.size() == 1) {
-        RetainedKnowledge RK = getKnowledgeFromOperandInAssume(
-            *cast<AssumeInst>(II), II->arg_size() + Idx);
-        if (!RK || RK.AttrKind != Attribute::NonNull)
-          continue;
+      case BundleAttr::NonNull: {
+        auto [Ptr] = llvm::getAssumeNonNullInfo(OBU);
 
         // Drop assume if we can prove nonnull without it
-        if (isKnownNonZero(RK.WasOn, getSimplifyQuery().getWithInstruction(II)))
+        if (isKnownNonZero(Ptr, getSimplifyQuery().getWithInstruction(II)))
           return CallBase::removeOperandBundleAt(II, Idx);
 
         // Fold the assume into metadata if it's valid at the load
-        if (auto *LI = dyn_cast<LoadInst>(RK.WasOn);
+        if (auto *LI = dyn_cast<LoadInst>(Ptr);
             LI &&
             isValidAssumeForContext(II, LI, &DT, /*AllowEphemerals=*/true)) {
           MDNode *MD = MDNode::get(II->getContext(), {});
@@ -3716,6 +3696,37 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
         }
 
         // TODO: apply nonnull return attributes to calls and invokes
+        break;
+      }
+
+      case BundleAttr::SeparateStorage: {
+        auto [Ptr1, Ptr2] = getAssumeSeparateStorageInfo(OBU);
+        // Separate storage assumptions apply to the underlying allocations, not
+        // any particular pointer within them. When evaluating the hints for AA
+        // purposes we getUnderlyingObject them; by precomputing the answers
+        // here we can avoid having to do so repeatedly there.
+        auto MaybeSimplifyHint = [&](const Use &U) {
+          Value *Hint = U.get();
+          // Not having a limit is safe because InstCombine removes unreachable
+          // code.
+          Value *UnderlyingObject = getUnderlyingObject(Hint, /*MaxLookup*/ 0);
+          if (Hint != UnderlyingObject)
+            replaceUse(const_cast<Use &>(U), UnderlyingObject);
+        };
+        MaybeSimplifyHint(Ptr1);
+        MaybeSimplifyHint(Ptr2);
+      } break;
+
+      // TODO: Drop these assumes when they are redundant
+      case BundleAttr::Dereferenceable:
+      case BundleAttr::DereferenceableOrNull:
+      case BundleAttr::Ignore:
+      case BundleAttr::NoUndef:
+        break;
+
+      // This cannot be simplified
+      case BundleAttr::Cold:
+        break;
       }
     }
 
diff --git a/llvm/lib/Transforms/Utils/AssumeBundleBuilder.cpp b/llvm/lib/Transforms/Utils/AssumeBundleBuilder.cpp
index 26bca70e1056f..722d04c9ae957 100644
--- a/llvm/lib/Transforms/Utils/AssumeBundleBuilder.cpp
+++ b/llvm/lib/Transforms/Utils/AssumeBundleBuilder.cpp
@@ -20,18 +20,12 @@
 #include "llvm/IR/Module.h"
 #include "llvm/IR/Operator.h"
 #include "llvm/Support/CommandLine.h"
-#include "llvm/Support/Compiler.h"
 #include "llvm/Support/DebugCounter.h"
 #include "llvm/Transforms/Utils/Local.h"
 
 using namespace llvm;
 
 namespace llvm {
-LLVM_ABI cl::opt<bool> ShouldPreserveAllAttributes(
-    "assume-preserve-all", cl::init(false), cl::Hidden,
-    cl::desc("enable preservation of all attributes. even those that are "
-             "unlikely to be useful"));
-
 cl::opt<bool> EnableKnowledgeRetention(
     "enable-knowledge-retention", cl::init(false), cl::Hidden,
     cl::desc(
@@ -193,8 +187,7 @@ struct AssumeBuilderState {
 
   void addAttribute(Attribute Attr, Value *WasOn) {
     if (Attr.isTypeAttribute() || Attr.isStringAttribute() ||
-        (!ShouldPreserveAllAttributes &&
-         !isUsefullToPreserve(Attr.getKindAsEnum())))
+        !isUsefullToPreserve(Attr.getKindAsEnum()))
       return;
     uint64_t AttrArg = 0;
     if (Attr.isIntAttribute())
diff --git a/llvm/lib/Transforms/Utils/PredicateInfo.cpp b/llvm/lib/Transforms/Utils/PredicateInfo.cpp
index 3d3c70522fb55..7a86d310f0f93 100644
--- a/llvm/lib/Transforms/Utils/PredicateInfo.cpp
+++ b/llvm/lib/Transforms/Utils/PredicateInfo.cpp
@@ -17,6 +17,7 @@
 #include "llvm/Analysis/AssumeBundleQueries.h"
 #include "llvm/Analysis/AssumptionCache.h"
 #include "llvm/IR/AssemblyAnnotationWriter.h"
+#include "llvm/IR/BundleAttributes.h"
 #include "llvm/IR/Dominators.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/InstIterator.h"
@@ -363,12 +364,12 @@ void PredicateInfoBuilder::processAssume(
     AssumeInst *II, BasicBlock *AssumeBB,
     SmallVectorImpl<Value *> &OpsToRename) {
   if (II->hasOperandBundles()) {
-    for (auto BOI : II->bundle_op_infos()) {
-      if (RetainedKnowledge RK = getKnowledgeFromBundle(*II, BOI)) {
-        if (RK.AttrKind == Attribute::NonNull && shouldRename(RK.WasOn))
-          addInfoFor(OpsToRename, RK.WasOn,
-                     new (Allocator) PredicateBundleAssume(RK.WasOn, II,
-                                                           Attribute::NonNull));
+    for (auto OBU : II->operand_bundles()) {
+      if (getBundleAttrFromOBU(OBU) == BundleAttr::NonNull) {
+        if (auto [Ptr] = getAssumeNonNullInfo(OBU); shouldRename(Ptr))
+          addInfoFor(OpsToRename, Ptr,
+                     new (Allocator)
+                         PredicateBundleAssume(Ptr, II, BundleAttr::NonNull));
       }
     }
     return;
@@ -724,7 +725,7 @@ PredicateInfo::PredicateInfo(Function &F, DominatorTree &DT,
 std::optional<PredicateConstraint> PredicateBase::getConstraint() const {
   switch (Type) {
   case PT_BundleAssume: {
-    assert(cast<PredicateBundleAssume>(this)->AttrKind == Attribute::NonNull &&
+    assert(cast<PredicateBundleAssume>(this)->AttrKind == BundleAttr::NonNull &&
            "Cannot handle anything other than NonNull");
     return {{CmpInst::ICMP_NE, ConstantPointerNull::get(
                                    cast<PointerType>(OriginalOp->getType()))}};
@@ -844,7 +845,7 @@ class PredicateInfoAnnotatedWriter : public AssemblyAnnotationWriter {
       } else if (const auto *PA = dyn_cast<PredicateAssume>(PI)) {
         OS << "; assume predicate info {";
         if (auto *PBA = dyn_cast<PredicateBundleAssume>(PA)) {
-          OS << " Attribute: " << Attribute::getNameFromAttrKind(PBA->AttrKind);
+          OS << " Attribute: " << getNameFromBundleAttr(PBA->AttrKind);
         } else {
           assert(isa<PredicateConditionAssume>(PA));
           OS << " Comparison:" << *PA->Condition;
diff --git a/llvm/test/Analysis/LazyValueAnalysis/lvi-after-jumpthreading.ll b/llvm/test/Analysis/LazyValueAnalysis/lvi-after-jumpthreading.ll
deleted file mode 100644
index 847882febdbb0..0000000000000
--- a/llvm/test/Analysis/LazyValueAnalysis/lvi-after-jumpthreading.ll
+++ /dev/null
@@ -1,189 +0,0 @@
-; RUN: opt < %s -passes="jump-threading,print<lazy-value-info>" -disable-output 2>&1 | FileCheck %s
-
-; Testing LVI cache after jump-threading
-
-; Jump-threading transforms the IR below to one where
-; loop and backedge basic blocks are merged into one.
-; basic block (named backedge) with the branch being:
-; %cont = icmp slt i32 %iv.next, 400
-; br i1 %cont, label %backedge, label %exit
-define i8 @test1(i32 %a, i32 %length) {
-; CHECK-LABEL: LVI for function 'test1':
-entry:
-; CHECK-LABEL: entry:
-; CHECK-NEXT:    ; LatticeVal for: 'i32 %a' is: overdefined
-; CHECK-NEXT:    ; LatticeVal for: 'i32 %length' is: overdefined
-  br label %loop
-
-; CHECK-LABEL: backedge:
-; CHECK-NEXT:     ; LatticeVal for: 'i32 %a' is: overdefined
-; CHECK-NEXT:     ; LatticeVal for: 'i32 %length' is: overdefined
-; CHECK-NEXT:     ; LatticeVal for: '  %iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]' in BB: '%backedge' is: constantrange<0, 400>
-; CHECK-NEXT:     ; LatticeVal for: '  %iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]' in BB: '%exit' is: constantrange<399, 400>
-; CHECK-NEXT:  %iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]
-; CHECK-NEXT:     ; LatticeVal for: '  %iv.next = add nsw i32 %iv, 1' in BB: '%backedge' is: constantrange<1, 401>
-; CHECK-NEXT:     ; LatticeVal for: '  %iv.next = add nsw i32 %iv, 1' in BB: '%exit' is: constantrange<400, 401>
-; CHECK-NEXT:  %iv.next = add nsw i32 %iv, 1
-; CHECK-NEXT:     ; LatticeVal for: '  %cont = icmp slt i32 %iv.next, 400' in BB: '%backedge' is: overdefined
-; CHECK-NEXT:     ; LatticeVal for: '  %cont = icmp slt i32 %iv.next, 400' in BB: '%exit' is: constantrange<0, -1>
-; CHECK-NEXT:  %cont = icmp slt i32 %iv.next, 400
-; CHECK-NOT: loop
-loop:
-  %iv = phi i32 [0, %entry], [%iv.next, %backedge]
-  %cnd = icmp sge i32 %iv, 0
-  br i1 %cnd, label %backedge, label %exit
-
-backedge:
-  %iv.next = add nsw i32 %iv, 1
-  %cont = icmp slt i32 %iv.next, 400
-  br i1 %cont, label %loop, label %exit
-
-exit:
-  ret i8 0
-}
-
-; Here JT does not transform the code, but LVICache is populated during the processing of blocks.
-define i8 @test2(i32 %n) {
-; CHECK-LABEL: LVI for function 'test2':
-; CHECK-LABEL: entry:
-; CHECK-NEXT:    ; LatticeVal for: 'i32 %n' is: overdefined
-; CHECK-NEXT: br label %loop
-entry:
-  br label %loop
-
-; CHECK-LABEL: loop:
-; CHECK-NEXT:    ; LatticeVal for: 'i32 %n' is: overdefined
-; CHECK-NEXT:    ; LatticeVal for: '  %iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]' in BB: '%loop' is: constantrange<0, 400>
-; CHECK-DAG:     ; LatticeVal for: '  %iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]' in BB: '%backedge' is: constantrange<0, -2147483648>
-; CHECK-DAG:     ; LatticeVal for: '  %iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]' in BB: '%exit' is: constantrange<0, -2147483648>
-; CHECK-NEXT:  %iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]
-loop:
-  %iv = phi i32 [0, %entry], [%iv.next, %backedge]
-; CHECK-NEXT:    ; LatticeVal for: '  %iv2 = phi i32 [ %n, %entry ], [ %iv2.next, %backedge ]' in BB: '%loop' is: overdefined
-; CHECK-DAG:     ; LatticeVal for: '  %iv2 = phi i32 [ %n, %entry ], [ %iv2.next, %backedge ]' in BB: '%backedge' is: constantrange<1, -2147483648>
-; CHECK-DAG:     ; LatticeVal for: '  %iv2 = phi i32 [ %n, %entry ], [ %iv2.next, %backedge ]' in BB: '%exit' is: overdefined
-; CHECK-NEXT:  %iv2 = phi i32 [ %n, %entry ], [ %iv2.next, %backedge ]
-  %iv2 = phi i32 [%n, %entry], [%iv2.next, %backedge]
-
-; CHECK-NEXT:    ; LatticeVal for: '  %cnd1 = icmp sge i32 %iv, 0' in BB: '%loop' is: overdefined
-; CHECK-DAG:     ; LatticeVal for: '  %cnd1 = icmp sge i32 %iv, 0' in BB: '%backedge' is: overdefined
-; CHECK-DAG:     ; LatticeVal for: '  %cnd1 = icmp sge i32 %iv, 0' in BB: '%exit' is: overdefined
-; CHECK-NEXT:  %cnd1 = icmp sge i32 %iv, 0
-  %cnd1 = icmp sge i32 %iv, 0
-  %cnd2 = icmp sgt i32 %iv2, 0
-; CHECK:       %cnd2 = icmp sgt i32 %iv2, 0
-; CHECK:         ; LatticeVal for: '  %cnd = and i1 %cnd1, %cnd2' in BB: '%loop' is: overdefined
-; CHECK-DAG:     ; LatticeVal for: '  %cnd = and i1 %cnd1, %cnd2' in BB: '%backedge' is: constantrange<-1, 0>
-; CHECK-DAG:     ; LatticeVal for: '  %cnd = and i1 %cnd1, %cnd2' in BB: '%exit' is: overdefined
-; CHECK-NEXT:  %cnd = and i1 %cnd1, %cnd2
-  %cnd = and i1 %cnd1, %cnd2
-  br i1 %cnd, label %backedge, label %exit
-
-; CHECK-LABEL: backedge:
-; CHECK-NEXT:    ; LatticeVal for: 'i32 %n' is: overdefined
-; CHECK-NEXT:    ; LatticeVal for: '  %iv.next = add nsw i32 %iv, 1' in BB: '%backedge' is: constantrange<1, -2147483648>
-; CHECK-NEXT:  %iv.next = add nsw i32 %iv, 1
-backedge:
-  %iv.next = add nsw i32 %iv, 1
-  %iv2.next = sub nsw i32 %iv2, 1
-; CHECK:         ; LatticeVal for: '  %cont1 = icmp slt i32 %iv.next, 400' in BB: '%backedge' is: overdefined
-; CHECK-NEXT:  %cont1 = icmp slt i32 %iv.next, 400
-  %cont1 = icmp slt i32 %iv.next, 400
-; CHECK-NEXT:    ; LatticeVal for: '  %cont2 = icmp sgt i32 %iv2.next, 0' in BB: '%backedge' is: overdefined
-; CHECK-NEXT:  %cont2 = icmp sgt i32 %iv2.next, 0
-  %cont2 = icmp sgt i32 %iv2.next, 0
-; CHECK-NEXT:    ; LatticeVal for: '  %cont = and i1 %cont1, %cont2' in BB: '%backedge' is: overdefined
-; CHECK-NEXT:  %cont = and i1 %cont1, %cont2
-  %cont = and i1 %cont1, %cont2
-  br i1 %cont, label %loop, label %exit
-
-exit:
-  ret i8 0
-}
-
-; Merging cont block into do block. Make sure that we do not incorrectly have the cont
-; LVI info as LVI info for the beginning of do block. LVI info for %i is Range[0,1)
-; at beginning of cont Block, which is incorrect at the beginning of do block.
-define i32 @test3(i32 %i, i1 %f, i32 %n) {
-; CHECK-LABEL: LVI for function 'test3':
-; CHECK-LABEL: entry
-; CHECK:  ; LatticeVal for: 'i32 %i' is: overdefined
-; CHECK: %c = icmp ne i32 %i, -2134
-; CHECK: br i1 %c, label %cont, label %exit
-entry:
-  %c = icmp ne i32 %i, -2134
-  br i1 %c, label %do, label %exit
-
-exit:
-  %c1 = icmp ne i32 %i, -42
-  br i1 %c1, label %exit2, label %exit
-
-; CHECK-LABEL: cont:
-; Here cont is merged to do and i is any value except -2134.
-; i is not the single value: zero.
-; CHECK-NOT:  ; LatticeVal for: 'i32 %i' is: constantrange<0, 1>
-; CHECK:      ; LatticeVal for: 'i32 %i' is: constantrange<-2133, -2134>
-; CHECK:      ; LatticeVal for: '  %cond.0 = icmp sgt i32 %i, 0' in BB: '%cont' is: overdefined
-; CHECK:   %cond.0 = icmp sgt i32 %i, 0
-; CHECK:   %consume = call i32 @consume
-; CHECK:   %cond = icmp eq i32 %i, 0
-; CHECK:   call void (i1, ...) @llvm.experimental.guard(i1 %cond)
-; CHECK:   %cond.3 = icmp sgt i32 %i, %n
-; CHECK:   br i1 %cond.3, label %exit2, label %exit
-cont:
-  %cond.3 = icmp sgt i32 %i, %n
-  br i1 %cond.3, label %exit2, label %exit
-
-do:
-  %cond.0 = icmp sgt i32 %i, 0
-  %consume = call i32 @consume(i1 %cond.0)
-  %cond = icmp eq i32 %i, 0
-  call void (i1, ...) @llvm.experimental.guard(i1 %cond) [ "deopt"() ]
-  %cond.2 = icmp sgt i32 %i, 0
-  br i1 %cond.2, label %exit, label %cont
-
-exit2:
-; CHECK-LABEL: exit2:
-; LatticeVal for: 'i32 %i' is: constantrange<-2134, 1>
-  ret i32 30
-}
-
-; FIXME: We should be able to merge cont into do.
-; When we do so, LVI for cont cannot be the one for the merged do block.
-define i32 @test4(i32 %i, i1 %f, i32 %n) {
-; CHECK-LABEL: LVI for function 'test4':
-entry:
-  %c = icmp ne i32 %i, -2134
-  br i1 %c, label %do, label %exit
-
-exit:                                             ; preds = %do, %cont, %exit, %entry
-  %c1 = icmp ne i32 %i, -42
-  br i1 %c1, label %exit2, label %exit
-
-cont:                                             ; preds = %do
-; CHECK-LABEL: cont:
-; CHECK:  ; LatticeVal for: 'i1 %f' is: constantrange<-1, 0>
-; CHECK: call void @dummy(i1 %f)
-  call void @dummy(i1 %f)
-  br label %exit2
-
-do:                                               ; preds = %entry
-; CHECK-LABEL: do:
-; CHECK:  ; LatticeVal for: 'i1 %f' is: overdefined
-; CHECK: call void @dummy(i1 %f)
-; CHECK: br i1 %cond, label %exit, label %cont
-  call void @dummy(i1 %f)
-  %consume = call i32 @exit()
-  call void @llvm.assume(i1 %f)
-  %cond = icmp eq i1 %f, false
-  br i1 %cond, label %exit, label %cont
-
-exit2:                                            ; preds = %cont, %exit
-  ret i32 30
-}
-
-declare i32 @exit()
-declare i32 @consume(i1)
-declare void @llvm.assume(i1) nounwind
-declare void @dummy(i1) nounwind
-declare void @llvm.experimental.guard(i1, ...)
diff --git a/llvm/test/Analysis/ValueTracking/assume-on-value.ll b/llvm/test/Analysis/ValueTracking/assume-on-value.ll
new file mode 100644
index 0000000000000..3565004ba9ba7
--- /dev/null
+++ b/llvm/test/Analysis/ValueTracking/assume-on-value.ll
@@ -0,0 +1,40 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=jump-threading,instcombine -S | FileCheck %s
+
+define { ptr, i32 } @assume_on_different_value(i32 %_Precision, i1 %cmp6) {
+; CHECK-LABEL: define { ptr, i32 } @assume_on_different_value(
+; CHECK-SAME: i32 [[_PRECISION:%.*]], i1 [[CMP6:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br i1 [[CMP6]], label %[[IF_END_I:.*]], label %[[IF_END16:.*]]
+; CHECK:       [[IF_END16]]:
+; CHECK-NEXT:    [[REF_TMP_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[_PRECISION]] to i64
+; CHECK-NEXT:    [[TMP0:%.*]] = inttoptr i64 [[REF_TMP_SROA_0_0_INSERT_EXT]] to ptr
+; CHECK-NEXT:    [[CMP_I:%.*]] = icmp sgt i32 [[_PRECISION]], 0
+; CHECK-NEXT:    br i1 [[CMP_I]], label %[[IF_THEN_I:.*]], label %[[IF_END_I]]
+; CHECK:       [[IF_THEN_I]]:
+; CHECK-NEXT:    ret { ptr, i32 } zeroinitializer
+; CHECK:       [[IF_END_I]]:
+; CHECK-NEXT:    [[TMP1:%.*]] = phi ptr [ [[TMP0]], %[[IF_END16]] ], [ null, %[[ENTRY]] ]
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[TMP1]], i64 8), "nonnull"(ptr [[TMP1]]), "dereferenceable"(ptr [[TMP1]], i64 1) ]
+; CHECK-NEXT:    ret { ptr, i32 } zeroinitializer
+;
+entry:
+  br i1 %cmp6, label %if.end16, label %if.else
+
+if.else:                                          ; preds = %entry
+  br label %if.end16
+
+if.end16:                                         ; preds = %if.else, %entry
+  %_Precision.addr.0 = phi i32 [ %_Precision, %if.else ], [ 0, %entry ]
+  %ref.tmp.sroa.0.0.insert.ext = zext i32 %_Precision.addr.0 to i64
+  %0 = inttoptr i64 %ref.tmp.sroa.0.0.insert.ext to ptr
+  %cmp.i = icmp sgt i32 %_Precision.addr.0, 0
+  br i1 %cmp.i, label %if.then.i, label %if.end.i
+
+if.then.i:                                        ; preds = %if.end16
+  ret { ptr, i32 } zeroinitializer
+
+if.end.i:                                         ; preds = %if.end16
+  call void @llvm.assume(i1 true) [ "align"(ptr %0, i64 8), "nonnull"(ptr %0), "dereferenceable"(ptr %0, i64 1) ]
+  ret { ptr, i32 } zeroinitializer
+}
diff --git a/llvm/test/Analysis/ValueTracking/assume-queries-counter.ll b/llvm/test/Analysis/ValueTracking/assume-queries-counter.ll
deleted file mode 100644
index 0cb8279f3db38..0000000000000
--- a/llvm/test/Analysis/ValueTracking/assume-queries-counter.ll
+++ /dev/null
@@ -1,112 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-
-; RUN: opt < %s -passes=instcombine --debug-counter=assume-queries-counter=1 -S | FileCheck %s --check-prefixes=COUNTER1
-; RUN: opt < %s -passes=instcombine --debug-counter=assume-queries-counter=2-3 -S | FileCheck %s --check-prefixes=COUNTER2
-; RUN: opt < %s -passes=instcombine --debug-counter=assume-queries-counter=4-7 -S | FileCheck %s --check-prefixes=COUNTER3
-
-declare i1 @get_val()
-declare void @llvm.assume(i1)
-
-define dso_local i1 @test1(ptr readonly %0) {
-; COUNTER1-LABEL: @test1(
-; COUNTER1-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[TMP0:%.*]]) ]
-; COUNTER1-NEXT:    ret i1 false
-;
-; COUNTER2-LABEL: @test1(
-; COUNTER2-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[TMP0:%.*]]) ]
-; COUNTER2-NEXT:    [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
-; COUNTER2-NEXT:    ret i1 [[TMP2]]
-;
-; COUNTER3-LABEL: @test1(
-; COUNTER3-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[TMP0:%.*]]) ]
-; COUNTER3-NEXT:    [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
-; COUNTER3-NEXT:    ret i1 [[TMP2]]
-;
-  call void @llvm.assume(i1 true) ["nonnull"(ptr %0)]
-  %2 = icmp eq ptr %0, null
-  ret i1 %2
-}
-
-define dso_local i1 @test2(ptr readonly %0) {
-; COUNTER1-LABEL: @test2(
-; COUNTER1-NEXT:    [[TMP2:%.*]] = icmp eq ptr [[TMP0:%.*]], null
-; COUNTER1-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[TMP0]]) ]
-; COUNTER1-NEXT:    ret i1 [[TMP2]]
-;
-; COUNTER2-LABEL: @test2(
-; COUNTER2-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[TMP0:%.*]]) ]
-; COUNTER2-NEXT:    ret i1 false
-;
-; COUNTER3-LABEL: @test2(
-; COUNTER3-NEXT:    [[TMP2:%.*]] = icmp eq ptr [[TMP0:%.*]], null
-; COUNTER3-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[TMP0]]) ]
-; COUNTER3-NEXT:    ret i1 [[TMP2]]
-;
-  %2 = icmp eq ptr %0, null
-  call void @llvm.assume(i1 true) ["nonnull"(ptr %0)]
-  ret i1 %2
-}
-
-define dso_local i32 @test4(ptr readonly %0, i1 %cond) nofree nosync {
-; COUNTER1-LABEL: @test4(
-; COUNTER1-NEXT:    call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0:%.*]], i32 4) ]
-; COUNTER1-NEXT:    br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]]
-; COUNTER1:       B:
-; COUNTER1-NEXT:    br label [[A]]
-; COUNTER1:       A:
-; COUNTER1-NEXT:    [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
-; COUNTER1-NEXT:    br i1 [[TMP2]], label [[TMP5:%.*]], label [[TMP3:%.*]]
-; COUNTER1:       3:
-; COUNTER1-NEXT:    [[TMP4:%.*]] = load i32, ptr [[TMP0]], align 4
-; COUNTER1-NEXT:    br label [[TMP5]]
-; COUNTER1:       5:
-; COUNTER1-NEXT:    [[TMP6:%.*]] = phi i32 [ [[TMP4]], [[TMP3]] ], [ 0, [[A]] ]
-; COUNTER1-NEXT:    ret i32 [[TMP6]]
-;
-; COUNTER2-LABEL: @test4(
-; COUNTER2-NEXT:    call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0:%.*]], i32 4) ]
-; COUNTER2-NEXT:    br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]]
-; COUNTER2:       B:
-; COUNTER2-NEXT:    br label [[A]]
-; COUNTER2:       A:
-; COUNTER2-NEXT:    [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
-; COUNTER2-NEXT:    br i1 [[TMP2]], label [[TMP5:%.*]], label [[TMP3:%.*]]
-; COUNTER2:       3:
-; COUNTER2-NEXT:    [[TMP4:%.*]] = load i32, ptr [[TMP0]], align 4
-; COUNTER2-NEXT:    br label [[TMP5]]
-; COUNTER2:       5:
-; COUNTER2-NEXT:    [[TMP6:%.*]] = phi i32 [ [[TMP4]], [[TMP3]] ], [ 0, [[A]] ]
-; COUNTER2-NEXT:    ret i32 [[TMP6]]
-;
-; COUNTER3-LABEL: @test4(
-; COUNTER3-NEXT:    call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0:%.*]], i32 4) ]
-; COUNTER3-NEXT:    br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]]
-; COUNTER3:       B:
-; COUNTER3-NEXT:    br label [[A]]
-; COUNTER3:       A:
-; COUNTER3-NEXT:    br i1 false, label [[TMP4:%.*]], label [[TMP2:%.*]]
-; COUNTER3:       2:
-; COUNTER3-NEXT:    [[TMP3:%.*]] = load i32, ptr [[TMP0]], align 4
-; COUNTER3-NEXT:    br label [[TMP4]]
-; COUNTER3:       4:
-; COUNTER3-NEXT:    [[TMP5:%.*]] = phi i32 [ [[TMP3]], [[TMP2]] ], [ poison, [[A]] ]
-; COUNTER3-NEXT:    ret i32 [[TMP5]]
-;
-  call void @llvm.assume(i1 true) ["dereferenceable"(ptr %0, i32 4)]
-  br i1 %cond, label %A, label %B
-
-B:
-  br label %A
-
-A:
-  %2 = icmp eq ptr %0, null
-  br i1 %2, label %5, label %3
-
-3:                                                ; preds = %1
-  %4 = load i32, ptr %0, align 4
-  br label %5
-
-5:                                                ; preds = %1, %3
-  %6 = phi i32 [ %4, %3 ], [ 0, %A ]
-  ret i32 %6
-}
diff --git a/llvm/test/Analysis/ValueTracking/assume.ll b/llvm/test/Analysis/ValueTracking/assume.ll
index dafc15c2b2d23..c5fe253634716 100644
--- a/llvm/test/Analysis/ValueTracking/assume.ll
+++ b/llvm/test/Analysis/ValueTracking/assume.ll
@@ -191,3 +191,72 @@ define i1 @test_dereferenceable_non_zero_size_is_nonnull(ptr %ptr) {
   %2 = icmp eq ptr %ptr, null
   ret i1 %2
 }
+
+define i1 @test_align_with_constant_offset_0(ptr %ptr) {
+; CHECK-LABEL: @test_align_with_constant_offset_0(
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 8, i64 0) ]
+; CHECK-NEXT:    ret i1 true
+;
+  call void @llvm.assume(i1 true) ["align"(ptr %ptr, i64 8, i64 0)]
+  %intptr = ptrtoint ptr %ptr to i64
+  %and = and i64 %intptr, 7
+  %is_aligned = icmp eq i64 %and, 0
+  ret i1 %is_aligned
+}
+
+define i1 @test_align_with_constant_offset_1(ptr %ptr) {
+; CHECK-LABEL: @test_align_with_constant_offset_1(
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 8, i64 1) ]
+; CHECK-NEXT:    [[INTPTR:%.*]] = ptrtoint ptr [[PTR]] to i64
+; CHECK-NEXT:    [[AND:%.*]] = and i64 [[INTPTR]], 7
+; CHECK-NEXT:    [[IS_ALIGNED:%.*]] = icmp eq i64 [[AND]], 0
+; CHECK-NEXT:    ret i1 [[IS_ALIGNED]]
+;
+  call void @llvm.assume(i1 true) ["align"(ptr %ptr, i64 8, i64 1)]
+  %intptr = ptrtoint ptr %ptr to i64
+  %and = and i64 %intptr, 7
+  %is_aligned = icmp eq i64 %and, 0
+  ret i1 %is_aligned
+}
+
+define i1 @test_align_with_constant_offset_4(ptr %ptr) {
+; CHECK-LABEL: @test_align_with_constant_offset_4(
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 8, i64 4) ]
+; CHECK-NEXT:    [[INTPTR:%.*]] = ptrtoint ptr [[PTR]] to i64
+; CHECK-NEXT:    [[AND:%.*]] = and i64 [[INTPTR]], 4
+; CHECK-NEXT:    [[IS_ALIGNED:%.*]] = icmp eq i64 [[AND]], 0
+; CHECK-NEXT:    ret i1 [[IS_ALIGNED]]
+;
+  call void @llvm.assume(i1 true) ["align"(ptr %ptr, i64 8, i64 4)]
+  %intptr = ptrtoint ptr %ptr to i64
+  %and = and i64 %intptr, 7
+  %is_aligned = icmp eq i64 %and, 0
+  ret i1 %is_aligned
+}
+
+define i1 @test_align_with_constant_offset_8(ptr %ptr) {
+; CHECK-LABEL: @test_align_with_constant_offset_8(
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 8, i64 8) ]
+; CHECK-NEXT:    ret i1 true
+;
+  call void @llvm.assume(i1 true) ["align"(ptr %ptr, i64 8, i64 8)]
+  %intptr = ptrtoint ptr %ptr to i64
+  %and = and i64 %intptr, 7
+  %is_aligned = icmp eq i64 %and, 0
+  ret i1 %is_aligned
+}
+
+define i1 @test_align_with_variable_offset(ptr %ptr, i64 %offset) {
+; CHECK-LABEL: @test_align_with_variable_offset(
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 8, i64 [[OFFSET:%.*]]) ]
+; CHECK-NEXT:    [[INTPTR:%.*]] = ptrtoint ptr [[PTR]] to i64
+; CHECK-NEXT:    [[AND:%.*]] = and i64 [[INTPTR]], 7
+; CHECK-NEXT:    [[IS_ALIGNED:%.*]] = icmp eq i64 [[AND]], 0
+; CHECK-NEXT:    ret i1 [[IS_ALIGNED]]
+;
+  call void @llvm.assume(i1 true) ["align"(ptr %ptr, i64 8, i64 %offset)]
+  %intptr = ptrtoint ptr %ptr to i64
+  %and = and i64 %intptr, 7
+  %is_aligned = icmp eq i64 %and, 0
+  ret i1 %is_aligned
+}
diff --git a/llvm/test/Transforms/AlignmentFromAssumptions/simple.ll b/llvm/test/Transforms/AlignmentFromAssumptions/simple.ll
index c2f2d21fafc40..d908c2a5206fa 100644
--- a/llvm/test/Transforms/AlignmentFromAssumptions/simple.ll
+++ b/llvm/test/Transforms/AlignmentFromAssumptions/simple.ll
@@ -285,7 +285,7 @@ define i32 @koo2(ptr nocapture %a) {
 ; CHECK-LABEL: define i32 @koo2
 ; CHECK-SAME: (ptr captures(none) [[A:%.*]]) {
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    tail call void @llvm.assume(i1 true) [ "align"(ptr [[A]], i128 32, i128 0) ]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 true) [ "align"(ptr [[A]], i64 32, i64 0) ]
 ; CHECK-NEXT:    br label [[FOR_BODY:%.*]]
 ; CHECK:       for.body:
 ; CHECK-NEXT:    [[INDVARS_IV:%.*]] = phi i64 [ -4, [[ENTRY:%.*]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ]
@@ -302,7 +302,7 @@ define i32 @koo2(ptr nocapture %a) {
 ; CHECK-NEXT:    ret i32 [[ADD_LCSSA]]
 ;
 entry:
-  tail call void @llvm.assume(i1 true) ["align"(ptr %a, i128 32, i128 0)]
+  tail call void @llvm.assume(i1 true) ["align"(ptr %a, i64 32, i64 0)]
   br label %for.body
 
 for.body:                                         ; preds = %entry, %for.body
@@ -399,4 +399,3 @@ declare void @llvm.assume(i1) nounwind
 
 declare void @llvm.memset.p0.i64(ptr nocapture, i8, i64, i1) nounwind
 declare void @llvm.memcpy.p0.p0.i64(ptr nocapture, ptr nocapture, i64, i1) nounwind
-
diff --git a/llvm/test/Transforms/Attributor/nofpclass.ll b/llvm/test/Transforms/Attributor/nofpclass.ll
index ac0aedc7716d5..8652049226c75 100644
--- a/llvm/test/Transforms/Attributor/nofpclass.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass.ll
@@ -644,34 +644,6 @@ entry:
   ret half %arg
 }
 
-define float @assume_bundles(i1 %c, float %ret) {
-; CHECK-LABEL: define float @assume_bundles
-; CHECK-SAME: (i1 noundef [[C:%.*]], float returned [[RET:%.*]]) {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    br i1 [[C]], label [[A:%.*]], label [[B:%.*]]
-; CHECK:       A:
-; CHECK-NEXT:    call void @llvm.assume(i1 noundef true) #[[ATTR22]] [ "nofpclass"(float [[RET]], i32 3) ]
-; CHECK-NEXT:    call void @extern.use(float nofpclass(nan) [[RET]])
-; CHECK-NEXT:    ret float [[RET]]
-; CHECK:       B:
-; CHECK-NEXT:    call void @llvm.assume(i1 noundef true) [ "nofpclass"(float [[RET]], i32 12) ]
-; CHECK-NEXT:    call void @extern.use(float nofpclass(ninf nnorm) [[RET]])
-; CHECK-NEXT:    ret float [[RET]]
-;
-entry:
-  br i1 %c, label %A, label %B
-
-A:
-  call void @llvm.assume(i1 true) [ "nofpclass"(float %ret, i32 3) ]
-  call void @extern.use(float %ret)
-  ret float %ret
-
-B:
-  call void @llvm.assume(i1 true) [ "nofpclass"(float %ret, i32 12) ]
-  call void @extern.use(float %ret)
-  ret float %ret
-}
-
 define float @returned_load(ptr %ptr) {
 ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
 ; CHECK-LABEL: define float @returned_load
diff --git a/llvm/test/Transforms/Attributor/nofree.ll b/llvm/test/Transforms/Attributor/nofree.ll
index 94aa79aa327f4..6937238dc8c3c 100644
--- a/llvm/test/Transforms/Attributor/nofree.ll
+++ b/llvm/test/Transforms/Attributor/nofree.ll
@@ -343,85 +343,6 @@ define void @test14(ptr nocapture %0, ptr nocapture %1) {
 
 ; UTC_ARGS: --enable
 
-define void @nonnull_assume_pos(ptr %arg1, ptr %arg2, ptr %arg3, ptr %arg4) {
-; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_pos
-; ATTRIBUTOR-SAME: (ptr nofree [[ARG1:%.*]], ptr [[ARG2:%.*]], ptr nofree [[ARG3:%.*]], ptr [[ARG4:%.*]])
-; ATTRIBUTOR-NEXT:    call void @llvm.assume(i1 true) #11 [ "nofree"(ptr [[ARG1]]), "nofree"(ptr [[ARG3]]) ]
-; ATTRIBUTOR-NEXT:    call void @unknown(ptr nofree [[ARG1]], ptr [[ARG2]], ptr nofree [[ARG3]], ptr [[ARG4]])
-; ATTRIBUTOR-NEXT:    ret void
-;
-; CHECK-LABEL: define {{[^@]+}}@nonnull_assume_pos
-; CHECK-SAME: (ptr nofree [[ARG1:%.*]], ptr [[ARG2:%.*]], ptr nofree [[ARG3:%.*]], ptr [[ARG4:%.*]]) {
-; CHECK-NEXT:    call void @llvm.assume(i1 noundef true) #[[ATTR15:[0-9]+]] [ "nofree"(ptr [[ARG1]]), "nofree"(ptr [[ARG3]]) ]
-; CHECK-NEXT:    call void @unknown(ptr nofree [[ARG1]], ptr [[ARG2]], ptr nofree [[ARG3]], ptr [[ARG4]])
-; CHECK-NEXT:    ret void
-;
-  call void @llvm.assume(i1 true) ["nofree"(ptr %arg1), "nofree"(ptr %arg3)]
-  call void @unknown(ptr %arg1, ptr %arg2, ptr %arg3, ptr %arg4)
-  ret void
-}
-define void @nonnull_assume_neg(ptr %arg1, ptr %arg2, ptr %arg3, ptr %arg4) {
-; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_neg
-; ATTRIBUTOR-SAME: (ptr [[ARG1:%.*]], ptr [[ARG2:%.*]], ptr [[ARG3:%.*]], ptr [[ARG4:%.*]])
-; ATTRIBUTOR-NEXT:    call void @unknown(ptr [[ARG1]], ptr [[ARG2]], ptr [[ARG3]], ptr [[ARG4]])
-; ATTRIBUTOR-NEXT:    call void @llvm.assume(i1 true) [ "nofree"(ptr [[ARG1]]), "nofree"(ptr [[ARG3]]) ]
-; ATTRIBUTOR-NEXT:    ret void
-;
-; CHECK-LABEL: define {{[^@]+}}@nonnull_assume_neg
-; CHECK-SAME: (ptr [[ARG1:%.*]], ptr [[ARG2:%.*]], ptr [[ARG3:%.*]], ptr [[ARG4:%.*]]) {
-; CHECK-NEXT:    call void @unknown(ptr [[ARG1]], ptr [[ARG2]], ptr [[ARG3]], ptr [[ARG4]])
-; CHECK-NEXT:    call void @llvm.assume(i1 noundef true) [ "nofree"(ptr [[ARG1]]), "nofree"(ptr [[ARG3]]) ]
-; CHECK-NEXT:    ret void
-;
-  call void @unknown(ptr %arg1, ptr %arg2, ptr %arg3, ptr %arg4)
-  call void @llvm.assume(i1 true) ["nofree"(ptr %arg1), "nofree"(ptr %arg3)]
-  ret void
-}
-define void @nonnull_assume_call(ptr %arg1, ptr %arg2, ptr %arg3, ptr %arg4) {
-; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_call
-; ATTRIBUTOR-SAME: (ptr [[ARG1:%.*]], ptr [[ARG2:%.*]], ptr [[ARG3:%.*]], ptr [[ARG4:%.*]])
-; ATTRIBUTOR-NEXT:    call void @unknown(ptr [[ARG1]], ptr [[ARG2]], ptr [[ARG3]], ptr [[ARG4]])
-; ATTRIBUTOR-NEXT:    call void @use_i8_ptr(ptr noalias readnone [[ARG1]])
-; ATTRIBUTOR-NEXT:    call void @use_i8_ptr(ptr noalias readnone [[ARG2]])
-; ATTRIBUTOR-NEXT:    call void @llvm.assume(i1 true) [ "nofree"(ptr [[ARG1]]), "nofree"(ptr [[ARG3]]) ]
-; ATTRIBUTOR-NEXT:    call void @use_i8_ptr(ptr noalias nofree readnone [[ARG3]])
-; ATTRIBUTOR-NEXT:    call void @use_i8_ptr(ptr noalias readnone [[ARG4]])
-; ATTRIBUTOR-NEXT:    call void @use_i8_ptr_ret(ptr noalias nofree readnone [[ARG1]])
-; ATTRIBUTOR-NEXT:    call void @use_i8_ptr_ret(ptr noalias readnone [[ARG2]])
-; ATTRIBUTOR-NEXT:    call void @llvm.assume(i1 true) [ "nofree"(ptr [[ARG1]]), "nofree"(ptr [[ARG4]]) ]
-; ATTRIBUTOR-NEXT:    call void @use_i8_ptr_ret(ptr noalias nofree readnone [[ARG3]])
-; ATTRIBUTOR-NEXT:    call void @use_i8_ptr_ret(ptr noalias nofree readnone [[ARG4]])
-; ATTRIBUTOR-NEXT:    ret void
-;
-; CHECK-LABEL: define {{[^@]+}}@nonnull_assume_call
-; CHECK-SAME: (ptr [[ARG1:%.*]], ptr [[ARG2:%.*]], ptr [[ARG3:%.*]], ptr [[ARG4:%.*]]) {
-; CHECK-NEXT:    call void @unknown(ptr [[ARG1]], ptr [[ARG2]], ptr [[ARG3]], ptr [[ARG4]])
-; CHECK-NEXT:    call void @use_i8_ptr(ptr noalias nofree readnone captures(none) [[ARG1]]) #[[ATTR0]]
-; CHECK-NEXT:    call void @use_i8_ptr(ptr noalias nofree readnone captures(none) [[ARG2]]) #[[ATTR0]]
-; CHECK-NEXT:    call void @llvm.assume(i1 noundef true) [ "nofree"(ptr [[ARG1]]), "nofree"(ptr [[ARG3]]) ]
-; CHECK-NEXT:    call void @use_i8_ptr(ptr noalias nofree readnone captures(none) [[ARG3]]) #[[ATTR0]]
-; CHECK-NEXT:    call void @use_i8_ptr(ptr noalias nofree readnone captures(none) [[ARG4]]) #[[ATTR0]]
-; CHECK-NEXT:    call void @use_i8_ptr_ret(ptr noalias nofree readnone captures(none) [[ARG1]]) #[[ATTR0]]
-; CHECK-NEXT:    call void @use_i8_ptr_ret(ptr noalias nofree readnone captures(none) [[ARG2]]) #[[ATTR0]]
-; CHECK-NEXT:    call void @llvm.assume(i1 noundef true) [ "nofree"(ptr [[ARG1]]), "nofree"(ptr [[ARG4]]) ]
-; CHECK-NEXT:    call void @use_i8_ptr_ret(ptr noalias nofree readnone captures(none) [[ARG3]]) #[[ATTR0]]
-; CHECK-NEXT:    call void @use_i8_ptr_ret(ptr noalias nofree readnone captures(none) [[ARG4]]) #[[ATTR0]]
-; CHECK-NEXT:    ret void
-;
-  call void @unknown(ptr %arg1, ptr %arg2, ptr %arg3, ptr %arg4)
-  call void @use_i8_ptr(ptr %arg1)
-  call void @use_i8_ptr(ptr %arg2)
-  call void @llvm.assume(i1 true) ["nofree"(ptr %arg1), "nofree"(ptr %arg3)]
-  call void @use_i8_ptr(ptr %arg3)
-  call void @use_i8_ptr(ptr %arg4)
-  call void @use_i8_ptr_ret(ptr %arg1)
-  call void @use_i8_ptr_ret(ptr %arg2)
-  call void @llvm.assume(i1 true) ["nofree"(ptr %arg1), "nofree"(ptr %arg4)]
-  call void @use_i8_ptr_ret(ptr %arg3)
-  call void @use_i8_ptr_ret(ptr %arg4)
-  ret void
-}
-
 ; FIXME: function is nofree
 define weak void @implied_nofree1() readnone {
 ; CHECK: Function Attrs: nosync memory(none)
@@ -498,7 +419,6 @@ attributes #2 = { nobuiltin nounwind }
 ; TUNIT: attributes #[[ATTR12:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }
 ; TUNIT: attributes #[[ATTR13:[0-9]+]] = { nounwind willreturn }
 ; TUNIT: attributes #[[ATTR14]] = { nofree nosync willreturn }
-; TUNIT: attributes #[[ATTR15]] = { nofree willreturn memory(write) }
 ;.
 ; CGSCC: attributes #[[ATTR0]] = { nounwind }
 ; CGSCC: attributes #[[ATTR1]] = { noinline nounwind uwtable }
@@ -515,5 +435,4 @@ attributes #2 = { nobuiltin nounwind }
 ; CGSCC: attributes #[[ATTR12:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }
 ; CGSCC: attributes #[[ATTR13:[0-9]+]] = { nounwind willreturn }
 ; CGSCC: attributes #[[ATTR14]] = { nofree nosync willreturn }
-; CGSCC: attributes #[[ATTR15]] = { nofree willreturn memory(write) }
 ;.
diff --git a/llvm/test/Transforms/InstCombine/assume.ll b/llvm/test/Transforms/InstCombine/assume.ll
index a154e254c138e..c1ccb49209227 100644
--- a/llvm/test/Transforms/InstCombine/assume.ll
+++ b/llvm/test/Transforms/InstCombine/assume.ll
@@ -121,6 +121,77 @@ entry:
   ret void
 }
 
+define void @align_with_constant_offset_0(ptr %ptr) {
+; DEFAULT-LABEL: @align_with_constant_offset_0(
+; DEFAULT-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 16) ]
+; DEFAULT-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[PTR]], i64 8, i64 0) ]
+; DEFAULT-NEXT:    ret void
+;
+; BUNDLES-LABEL: @align_with_constant_offset_0(
+; BUNDLES-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 16, i64 0) ]
+; BUNDLES-NEXT:    ret void
+;
+  call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i64 16) ]
+  call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i64 8, i64 0) ]
+  ret void
+}
+
+define void @align_with_constant_offset_1(ptr %ptr) {
+; CHECK-LABEL: @align_with_constant_offset_1(
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 16) ]
+; CHECK-NEXT:    [[PTR2:%.*]] = getelementptr i8, ptr [[PTR]], i64 9
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[PTR2]], i64 8, i64 1) ]
+; CHECK-NEXT:    ret void
+;
+  call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i64 16) ]
+  %ptr2 = getelementptr i8, ptr %ptr, i64 9
+  call void @llvm.assume(i1 true) [ "align"(ptr %ptr2, i64 8, i64 1) ]
+  ret void
+}
+
+define void @align_with_constant_offset_4(ptr %ptr) {
+; CHECK-LABEL: @align_with_constant_offset_4(
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 16) ]
+; CHECK-NEXT:    [[PTR2:%.*]] = getelementptr i8, ptr [[PTR]], i64 4
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[PTR2]], i64 8, i64 4) ]
+; CHECK-NEXT:    ret void
+;
+  call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i64 16) ]
+  %ptr2 = getelementptr i8, ptr %ptr, i64 4
+  call void @llvm.assume(i1 true) [ "align"(ptr %ptr2, i64 8, i64 4) ]
+  ret void
+}
+
+define void @align_with_constant_offset_8(ptr %ptr) {
+; DEFAULT-LABEL: @align_with_constant_offset_8(
+; DEFAULT-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 16) ]
+; DEFAULT-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[PTR]], i64 8, i64 8) ]
+; DEFAULT-NEXT:    ret void
+;
+; BUNDLES-LABEL: @align_with_constant_offset_8(
+; BUNDLES-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 16, i64 8) ]
+; BUNDLES-NEXT:    ret void
+;
+  call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i64 16) ]
+  call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i64 8, i64 8) ]
+  ret void
+}
+
+define void @align_with_variable_offset(ptr %ptr, i64 %offset) {
+; DEFAULT-LABEL: @align_with_variable_offset(
+; DEFAULT-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 16) ]
+; DEFAULT-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[PTR]], i64 8, i64 [[OFFSET:%.*]]) ]
+; DEFAULT-NEXT:    ret void
+;
+; BUNDLES-LABEL: @align_with_variable_offset(
+; BUNDLES-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 16, i64 [[OFFSET:%.*]]) ]
+; BUNDLES-NEXT:    ret void
+;
+  call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i64 16) ]
+  call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i64 8, i64 %offset) ]
+  ret void
+}
+
 define void @redundant_align() {
 ; CHECK-LABEL: @redundant_align(
 ; CHECK-NEXT:    [[PTR:%.*]] = call ptr @get_ptr()
diff --git a/llvm/test/Transforms/Util/assume-builder.ll b/llvm/test/Transforms/Util/assume-builder.ll
index 98ff2a4827d61..d5fc4ef57015d 100644
--- a/llvm/test/Transforms/Util/assume-builder.ll
+++ b/llvm/test/Transforms/Util/assume-builder.ll
@@ -1,6 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature
 ; RUN: opt -passes='assume-builder,verify' --enable-knowledge-retention -S %s | FileCheck %s --check-prefixes=BASIC
-; RUN: opt -passes='assume-builder,verify' --enable-knowledge-retention --assume-preserve-all -S %s | FileCheck %s --check-prefixes=ALL
 ; RUN: opt -passes='require<assumptions>,assume-builder,verify' --enable-knowledge-retention -S %s | FileCheck %s --check-prefixes=WITH-AC
 ; RUN: opt -passes='require<domtree>,require<assumptions>,assume-builder,verify' --enable-knowledge-retention -S %s | FileCheck %s --check-prefixes=CROSS-BLOCK
 ; RUN: opt -passes='assume-builder,require<domtree>,require<assumptions>,assume-simplify,verify' --enable-knowledge-retention -S %s | FileCheck %s --check-prefixes=FULL-SIMPLIFY
@@ -47,34 +46,6 @@ define void @test(ptr %P, ptr %P1, ptr %P2, ptr %P3) {
 ; BASIC-NEXT:    call void @func(ptr noundef nonnull [[P1]], ptr noundef nonnull [[P]])
 ; BASIC-NEXT:    ret void
 ;
-; ALL-LABEL: define {{[^@]+}}@test
-; ALL-SAME: (ptr [[P:%.*]], ptr [[P1:%.*]], ptr [[P2:%.*]], ptr [[P3:%.*]]) {
-; ALL-NEXT:  bb:
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[P]]), "dereferenceable"(ptr [[P]], i64 16) ]
-; ALL-NEXT:    call void @func(ptr nonnull dereferenceable(16) [[P]], ptr null)
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P1]], i64 12) ]
-; ALL-NEXT:    call void @func(ptr dereferenceable(12) [[P1]], ptr nonnull [[P]])
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "cold"(), "nounwind"(), "willreturn"() ]
-; ALL-NEXT:    call void @func_cold(ptr dereferenceable(12) [[P1]]) #[[ATTR6:[0-9]+]]
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "cold"(), "nounwind"(), "willreturn"() ]
-; ALL-NEXT:    call void @func_cold(ptr dereferenceable(12) [[P1]])
-; ALL-NEXT:    call void @func(ptr [[P1]], ptr [[P]])
-; ALL-NEXT:    call void @func_strbool(ptr [[P1]])
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 32) ]
-; ALL-NEXT:    call void @func(ptr dereferenceable(32) [[P]], ptr dereferenceable(8) [[P]])
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "norecurse"(), "nounwind"(), "willreturn"() ]
-; ALL-NEXT:    call void @func_many(ptr align 8 [[P1]])
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "noundef"(ptr [[P1]]), "align"(ptr [[P1]], i64 8), "norecurse"(), "nounwind"(), "willreturn"() ]
-; ALL-NEXT:    call void @func_many(ptr noundef align 8 [[P1]])
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "nounwind"() ]
-; ALL-NEXT:    call void @func_argattr(ptr [[P2]], ptr [[P3]])
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "noundef"(ptr [[P2]]), "align"(ptr [[P2]], i64 8), "noundef"(ptr [[P3]]), "nonnull"(ptr [[P3]]), "nounwind"() ]
-; ALL-NEXT:    call void @func_argattr2(ptr [[P2]], ptr [[P3]])
-; ALL-NEXT:    call void @func(ptr nonnull [[P1]], ptr nonnull [[P]])
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[P1]]), "noundef"(ptr [[P]]) ]
-; ALL-NEXT:    call void @func(ptr noundef nonnull [[P1]], ptr noundef nonnull [[P]])
-; ALL-NEXT:    ret void
-;
 ; WITH-AC-LABEL: define {{[^@]+}}@test
 ; WITH-AC-SAME: (ptr [[P:%.*]], ptr [[P1:%.*]], ptr [[P2:%.*]], ptr [[P3:%.*]]) {
 ; WITH-AC-NEXT:  bb:
@@ -201,38 +172,6 @@ define i32 @test2(ptr %arg, ptr %arg1, ptr %arg2) {
 ; BASIC-NEXT:    [[I26:%.*]] = add nsw i32 [[I21]], [[I25]]
 ; BASIC-NEXT:    ret i32 [[I26]]
 ;
-; ALL-LABEL: define {{[^@]+}}@test2
-; ALL-SAME: (ptr [[ARG:%.*]], ptr [[ARG1:%.*]], ptr [[ARG2:%.*]]) {
-; ALL-NEXT:  bb:
-; ALL-NEXT:    [[I:%.*]] = alloca ptr, align 8
-; ALL-NEXT:    [[I3:%.*]] = alloca ptr, align 8
-; ALL-NEXT:    [[I4:%.*]] = alloca ptr, align 8
-; ALL-NEXT:    [[I5:%.*]] = alloca [[STRUCT_S:%.*]], align 8
-; ALL-NEXT:    store ptr [[ARG]], ptr [[I]], align 8
-; ALL-NEXT:    store ptr [[ARG1]], ptr [[I3]], align 8
-; ALL-NEXT:    store ptr [[ARG2]], ptr [[I4]], align 8
-; ALL-NEXT:    [[I6:%.*]] = load ptr, ptr [[I3]], align 8
-; ALL-NEXT:    [[I7:%.*]] = load i32, ptr [[I6]], align 4
-; ALL-NEXT:    [[I8:%.*]] = trunc i32 [[I7]] to i8
-; ALL-NEXT:    [[I9:%.*]] = load ptr, ptr [[I4]], align 8
-; ALL-NEXT:    store i8 [[I8]], ptr [[I9]], align 1
-; ALL-NEXT:    [[I11:%.*]] = load ptr, ptr [[I]], align 8
-; ALL-NEXT:    [[I14:%.*]] = load ptr, ptr [[I]], align 8
-; ALL-NEXT:    [[I16:%.*]] = load i32, ptr [[I14]], align 8
-; ALL-NEXT:    [[I17:%.*]] = load ptr, ptr [[I]], align 8
-; ALL-NEXT:    [[I18:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[I17]], i32 0, i32 1
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[I17]], i64 5), "nonnull"(ptr [[I17]]), "align"(ptr [[I17]], i64 4) ]
-; ALL-NEXT:    [[I19:%.*]] = load i8, ptr [[I18]], align 4
-; ALL-NEXT:    [[I20:%.*]] = sext i8 [[I19]] to i32
-; ALL-NEXT:    [[I21:%.*]] = add nsw i32 [[I16]], [[I20]]
-; ALL-NEXT:    [[I22:%.*]] = load ptr, ptr [[I]], align 8
-; ALL-NEXT:    [[I23:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[I22]], i32 0, i32 2
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[I22]], i64 16), "nonnull"(ptr [[I22]]), "align"(ptr [[I22]], i64 8) ]
-; ALL-NEXT:    [[I24:%.*]] = load ptr, ptr [[I23]], align 8
-; ALL-NEXT:    [[I25:%.*]] = load i32, ptr [[I24]], align 4
-; ALL-NEXT:    [[I26:%.*]] = add nsw i32 [[I21]], [[I25]]
-; ALL-NEXT:    ret i32 [[I26]]
-;
 ; WITH-AC-LABEL: define {{[^@]+}}@test2
 ; WITH-AC-SAME: (ptr [[ARG:%.*]], ptr [[ARG1:%.*]], ptr [[ARG2:%.*]]) {
 ; WITH-AC-NEXT:  bb:
@@ -391,39 +330,6 @@ define i32 @test3(ptr %arg, ptr %arg1, ptr %arg2) #4 {
 ; BASIC-NEXT:    [[I26:%.*]] = add nsw i32 [[I21]], [[I25]]
 ; BASIC-NEXT:    ret i32 [[I26]]
 ;
-; ALL-LABEL: define {{[^@]+}}@test3
-; ALL-SAME: (ptr [[ARG:%.*]], ptr [[ARG1:%.*]], ptr [[ARG2:%.*]]) #[[ATTR4:[0-9]+]] {
-; ALL-NEXT:  bb:
-; ALL-NEXT:    [[I:%.*]] = alloca ptr, align 8
-; ALL-NEXT:    [[I3:%.*]] = alloca ptr, align 8
-; ALL-NEXT:    [[I4:%.*]] = alloca ptr, align 8
-; ALL-NEXT:    [[I5:%.*]] = alloca [[STRUCT_S:%.*]], align 8
-; ALL-NEXT:    store ptr [[ARG]], ptr [[I]], align 8
-; ALL-NEXT:    store ptr [[ARG1]], ptr [[I3]], align 8
-; ALL-NEXT:    store ptr [[ARG2]], ptr [[I4]], align 8
-; ALL-NEXT:    [[I6:%.*]] = load ptr, ptr [[I3]], align 8
-; ALL-NEXT:    [[I7:%.*]] = load i32, ptr [[I6]], align 4
-; ALL-NEXT:    [[I8:%.*]] = trunc i32 [[I7]] to i8
-; ALL-NEXT:    [[I9:%.*]] = load ptr, ptr [[I4]], align 8
-; ALL-NEXT:    store i8 [[I8]], ptr [[I9]], align 1
-; ALL-NEXT:    [[I11:%.*]] = load ptr, ptr [[I]], align 32
-; ALL-NEXT:    call void @may_throw()
-; ALL-NEXT:    [[I14:%.*]] = load ptr, ptr [[I]], align 8
-; ALL-NEXT:    [[I16:%.*]] = load i32, ptr [[I14]], align 8
-; ALL-NEXT:    [[I17:%.*]] = load ptr, ptr [[I]], align 8
-; ALL-NEXT:    [[I18:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[I17]], i32 0, i32 1
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[I17]], i64 5), "align"(ptr [[I17]], i64 4) ]
-; ALL-NEXT:    [[I19:%.*]] = load i8, ptr [[I18]], align 8
-; ALL-NEXT:    [[I20:%.*]] = sext i8 [[I19]] to i32
-; ALL-NEXT:    [[I21:%.*]] = add nsw i32 [[I16]], [[I20]]
-; ALL-NEXT:    [[I22:%.*]] = load ptr, ptr [[I]], align 8
-; ALL-NEXT:    [[I23:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[I22]], i32 0, i32 2
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[I22]], i64 16), "align"(ptr [[I22]], i64 8) ]
-; ALL-NEXT:    [[I24:%.*]] = load ptr, ptr [[I23]], align 8
-; ALL-NEXT:    [[I25:%.*]] = load i32, ptr [[I24]], align 4
-; ALL-NEXT:    [[I26:%.*]] = add nsw i32 [[I21]], [[I25]]
-; ALL-NEXT:    ret i32 [[I26]]
-;
 ; WITH-AC-LABEL: define {{[^@]+}}@test3
 ; WITH-AC-SAME: (ptr [[ARG:%.*]], ptr [[ARG1:%.*]], ptr [[ARG2:%.*]]) #[[ATTR4:[0-9]+]] {
 ; WITH-AC-NEXT:  bb:
@@ -577,30 +483,6 @@ define dso_local i32 @_Z6squarePi(ptr %P, ptr %P1, i1 %cond) {
 ; BASIC-NEXT:    store i32 0, ptr [[P1]], align 4
 ; BASIC-NEXT:    ret i32 0
 ;
-; ALL-LABEL: define {{[^@]+}}@_Z6squarePi
-; ALL-SAME: (ptr [[P:%.*]], ptr [[P1:%.*]], i1 [[COND:%.*]]) {
-; ALL-NEXT:  bb:
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 4), "nonnull"(ptr [[P]]), "align"(ptr [[P]], i64 4) ]
-; ALL-NEXT:    store i32 0, ptr [[P]], align 4
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P1]], i64 4), "nonnull"(ptr [[P1]]), "align"(ptr [[P1]], i64 8) ]
-; ALL-NEXT:    store i32 0, ptr [[P1]], align 8
-; ALL-NEXT:    br i1 [[COND]], label [[A:%.*]], label [[B:%.*]]
-; ALL:       A:
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[P]], i64 8) ]
-; ALL-NEXT:    store i32 0, ptr [[P]], align 8
-; ALL-NEXT:    store i32 0, ptr [[P1]], align 4
-; ALL-NEXT:    br i1 [[COND]], label [[C:%.*]], label [[B]]
-; ALL:       B:
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[P]], i64 8) ]
-; ALL-NEXT:    store i32 0, ptr [[P]], align 8
-; ALL-NEXT:    store i32 0, ptr [[P1]], align 8
-; ALL-NEXT:    br label [[C]]
-; ALL:       C:
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[P]], i64 32) ]
-; ALL-NEXT:    store i32 0, ptr [[P]], align 32
-; ALL-NEXT:    store i32 0, ptr [[P1]], align 4
-; ALL-NEXT:    ret i32 0
-;
 ; WITH-AC-LABEL: define {{[^@]+}}@_Z6squarePi
 ; WITH-AC-SAME: (ptr [[P:%.*]], ptr [[P1:%.*]], i1 [[COND:%.*]]) {
 ; WITH-AC-NEXT:  bb:
@@ -714,27 +596,6 @@ define dso_local i32 @test4A(ptr %arg, ptr %arg1, i32 %arg2, i32 %arg3) {
 ; BASIC:       bb10:
 ; BASIC-NEXT:    ret i32 0
 ;
-; ALL-LABEL: define {{[^@]+}}@test4A
-; ALL-SAME: (ptr [[ARG:%.*]], ptr [[ARG1:%.*]], i32 [[ARG2:%.*]], i32 [[ARG3:%.*]]) {
-; ALL-NEXT:  bb:
-; ALL-NEXT:    [[I:%.*]] = icmp ne ptr [[ARG1]], null
-; ALL-NEXT:    br i1 [[I]], label [[BB4:%.*]], label [[BB10:%.*]]
-; ALL:       bb4:
-; ALL-NEXT:    [[I5:%.*]] = add nsw i32 [[ARG3]], [[ARG2]]
-; ALL-NEXT:    call void @may_throw()
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[ARG]], i64 4), "nonnull"(ptr [[ARG]]), "align"(ptr [[ARG]], i64 4) ]
-; ALL-NEXT:    [[I6:%.*]] = load i32, ptr [[ARG]], align 4
-; ALL-NEXT:    [[I7:%.*]] = add nsw i32 [[I5]], [[I6]]
-; ALL-NEXT:    store i32 0, ptr [[ARG]], align 4
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[ARG1]], i64 4), "nonnull"(ptr [[ARG1]]), "align"(ptr [[ARG1]], i64 4) ]
-; ALL-NEXT:    [[I8:%.*]] = load i32, ptr [[ARG1]], align 4
-; ALL-NEXT:    [[I9:%.*]] = add nsw i32 [[I7]], [[I8]]
-; ALL-NEXT:    call void @may_throw()
-; ALL-NEXT:    store i32 [[I9]], ptr [[ARG1]], align 4
-; ALL-NEXT:    br label [[BB10]]
-; ALL:       bb10:
-; ALL-NEXT:    ret i32 0
-;
 ; WITH-AC-LABEL: define {{[^@]+}}@test4A
 ; WITH-AC-SAME: (ptr [[ARG:%.*]], ptr [[ARG1:%.*]], i32 [[ARG2:%.*]], i32 [[ARG3:%.*]]) {
 ; WITH-AC-NEXT:  bb:
@@ -833,19 +694,6 @@ define dso_local i32 @terminator(ptr %P) personality ptr @__gxx_personality_v0 {
 ; BASIC-NEXT:    [[DOT0:%.*]] = phi i32 [ 1, [[BB:%.*]] ], [ 0, [[CATCH]] ]
 ; BASIC-NEXT:    ret i32 [[DOT0]]
 ;
-; ALL-LABEL: define {{[^@]+}}@terminator
-; ALL-SAME: (ptr [[P:%.*]]) personality ptr @__gxx_personality_v0 {
-; ALL-NEXT:  bb:
-; ALL-NEXT:    invoke void @may_throwv2(ptr nonnull [[P]])
-; ALL-NEXT:            to label [[EXIT:%.*]] unwind label [[CATCH:%.*]]
-; ALL:       Catch:
-; ALL-NEXT:    [[V:%.*]] = landingpad { ptr, i32 }
-; ALL-NEXT:            catch ptr null
-; ALL-NEXT:    br label [[EXIT]]
-; ALL:       Exit:
-; ALL-NEXT:    [[DOT0:%.*]] = phi i32 [ 1, [[BB:%.*]] ], [ 0, [[CATCH]] ]
-; ALL-NEXT:    ret i32 [[DOT0]]
-;
 ; WITH-AC-LABEL: define {{[^@]+}}@terminator
 ; WITH-AC-SAME: (ptr [[P:%.*]]) personality ptr @__gxx_personality_v0 {
 ; WITH-AC-NEXT:  bb:
@@ -918,22 +766,6 @@ define dso_local i32 @test5(ptr %arg, i32 %arg1) {
 ; BASIC-NEXT:    [[I9:%.*]] = trunc i64 [[I8]] to i32
 ; BASIC-NEXT:    ret i32 [[I9]]
 ;
-; ALL-LABEL: define {{[^@]+}}@test5
-; ALL-SAME: (ptr [[ARG:%.*]], i32 [[ARG1:%.*]]) {
-; ALL-NEXT:  bb:
-; ALL-NEXT:    [[I3:%.*]] = sext i32 [[ARG1]] to i64
-; ALL-NEXT:    [[I4:%.*]] = getelementptr inbounds i16, ptr [[ARG]], i64 [[I3]]
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[I4]], i64 2), "nonnull"(ptr [[ARG]]), "align"(ptr [[ARG]], i64 8) ]
-; ALL-NEXT:    [[I5:%.*]] = load i16, ptr [[I4]], align 2
-; ALL-NEXT:    [[A:%.*]] = load i16, ptr [[I4]], align 4
-; ALL-NEXT:    [[I6:%.*]] = sext i16 [[I5]] to i64
-; ALL-NEXT:    [[I7:%.*]] = getelementptr inbounds i64, ptr [[ARG]], i64 [[I6]]
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[I7]], i64 8) ]
-; ALL-NEXT:    [[I8:%.*]] = load i64, ptr [[I7]], align 16
-; ALL-NEXT:    [[B:%.*]] = load i64, ptr [[I7]], align 32
-; ALL-NEXT:    [[I9:%.*]] = trunc i64 [[I8]] to i32
-; ALL-NEXT:    ret i32 [[I9]]
-;
 ; WITH-AC-LABEL: define {{[^@]+}}@test5
 ; WITH-AC-SAME: (ptr [[ARG:%.*]], i32 [[ARG1:%.*]]) {
 ; WITH-AC-NEXT:  bb:
@@ -1025,36 +857,6 @@ define i32 @test6(ptr %arg, i32 %arg1, ptr %arg2) {
 ; BASIC-NEXT:    [[I18:%.*]] = load i32, ptr [[I17]], align 4
 ; BASIC-NEXT:    ret i32 [[I18]]
 ;
-; ALL-LABEL: define {{[^@]+}}@test6
-; ALL-SAME: (ptr [[ARG:%.*]], i32 [[ARG1:%.*]], ptr [[ARG2:%.*]]) {
-; ALL-NEXT:  bb:
-; ALL-NEXT:    br label [[BB3:%.*]]
-; ALL:       bb3:
-; ALL-NEXT:    [[DOT0:%.*]] = phi i32 [ 0, [[BB:%.*]] ], [ [[I14:%.*]], [[BB4:%.*]] ]
-; ALL-NEXT:    [[I:%.*]] = icmp slt i32 [[DOT0]], [[ARG1]]
-; ALL-NEXT:    br i1 [[I]], label [[BB4]], label [[BB15:%.*]]
-; ALL:       bb4:
-; ALL-NEXT:    [[I5:%.*]] = add nsw i32 [[ARG1]], [[DOT0]]
-; ALL-NEXT:    [[I6:%.*]] = sext i32 [[I5]] to i64
-; ALL-NEXT:    [[I7:%.*]] = getelementptr inbounds i32, ptr [[ARG]], i64 [[I6]]
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[ARG]]), "align"(ptr [[ARG]], i64 4) ]
-; ALL-NEXT:    [[I8:%.*]] = load i32, ptr [[I7]], align 4
-; ALL-NEXT:    [[I9:%.*]] = mul nsw i32 [[DOT0]], [[I8]]
-; ALL-NEXT:    [[I10:%.*]] = sext i32 [[DOT0]] to i64
-; ALL-NEXT:    [[I11:%.*]] = getelementptr inbounds i32, ptr [[ARG]], i64 [[I10]]
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[I11]], i64 4) ]
-; ALL-NEXT:    [[I12:%.*]] = load i32, ptr [[I11]], align 4
-; ALL-NEXT:    [[I13:%.*]] = add nsw i32 [[I12]], [[I9]]
-; ALL-NEXT:    store i32 [[I13]], ptr [[I11]], align 4
-; ALL-NEXT:    [[I14]] = add nsw i32 [[DOT0]], 1
-; ALL-NEXT:    br label [[BB3]]
-; ALL:       bb15:
-; ALL-NEXT:    [[I16:%.*]] = sext i32 [[ARG1]] to i64
-; ALL-NEXT:    [[I17:%.*]] = getelementptr inbounds i32, ptr [[ARG2]], i64 [[I16]]
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[ARG2]]), "align"(ptr [[ARG2]], i64 4) ]
-; ALL-NEXT:    [[I18:%.*]] = load i32, ptr [[I17]], align 4
-; ALL-NEXT:    ret i32 [[I18]]
-;
 ; WITH-AC-LABEL: define {{[^@]+}}@test6
 ; WITH-AC-SAME: (ptr [[ARG:%.*]], i32 [[ARG1:%.*]], ptr [[ARG2:%.*]]) {
 ; WITH-AC-NEXT:  bb:
@@ -1193,25 +995,6 @@ define i32 @test7(ptr nonnull %arg, i32 %arg1) {
 ; BASIC-NEXT:    [[I11:%.*]] = load i32, ptr [[I10]], align 4
 ; BASIC-NEXT:    ret i32 [[I11]]
 ;
-; ALL-LABEL: define {{[^@]+}}@test7
-; ALL-SAME: (ptr nonnull [[ARG:%.*]], i32 [[ARG1:%.*]]) {
-; ALL-NEXT:  bb:
-; ALL-NEXT:    [[I:%.*]] = sext i32 [[ARG1]] to i64
-; ALL-NEXT:    [[I2:%.*]] = getelementptr inbounds [[STRUCT_A:%.*]], ptr [[ARG]], i64 0, i32 3
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[ARG]], i64 280), "align"(ptr [[ARG]], i64 16) ]
-; ALL-NEXT:    [[I3:%.*]] = load i64, ptr [[I2]], align 8
-; ALL-NEXT:    [[I4:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[ARG]], i64 0, i32 2, i64 [[I]], i64 [[I3]], i32 0
-; ALL-NEXT:    [[I5:%.*]] = load i64, ptr [[I4]], align 32
-; ALL-NEXT:    [[I6:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[ARG]], i64 0, i32 1
-; ALL-NEXT:    [[I7:%.*]] = load ptr, ptr [[I6]], align 8
-; ALL-NEXT:    [[I8:%.*]] = getelementptr inbounds i64, ptr [[I7]], i64 [[I3]]
-; ALL-NEXT:    call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[I8]], i64 8), "nonnull"(ptr [[I7]]), "align"(ptr [[I7]], i64 8) ]
-; ALL-NEXT:    store i64 [[I5]], ptr [[I8]], align 8
-; ALL-NEXT:    store i64 [[I5]], ptr [[I8]], align 8
-; ALL-NEXT:    [[I10:%.*]] = load ptr, ptr [[ARG]], align 8
-; ALL-NEXT:    [[I11:%.*]] = load i32, ptr [[I10]], align 4
-; ALL-NEXT:    ret i32 [[I11]]
-;
 ; WITH-AC-LABEL: define {{[^@]+}}@test7
 ; WITH-AC-SAME: (ptr nonnull [[ARG:%.*]], i32 [[ARG1:%.*]]) {
 ; WITH-AC-NEXT:  bb:
diff --git a/llvm/test/Verifier/assume-bundles.ll b/llvm/test/Verifier/assume-bundles.ll
index 728b118c99fb6..efba13dd7c7c5 100644
--- a/llvm/test/Verifier/assume-bundles.ll
+++ b/llvm/test/Verifier/assume-bundles.ll
@@ -7,32 +7,89 @@ define void @func(ptr %P, i32 %P1, ptr %P2, ptr %P3, i1 %cond) {
 ; CHECK: tags must be valid attribute names
 ; CHECK: "adazdazd"
   call void @llvm.assume(i1 true) ["adazdazd"()]
+; CHECK: assume with operand bundles must have i1 true condition
+  call void @llvm.assume(i1 %cond) ["nonnull"(ptr %P)]
+
+; Align checks
+;
+; CHECK: alignment assumptions should have 2 or 3 arguments
+  call void @llvm.assume(i1 true) ["align"(ptr %P)]
+; This one is valid
+  call void @llvm.assume(i1 true) ["align"(ptr %P, i32 %P1)]
+; This one is valid
+  call void @llvm.assume(i1 true) ["align"(ptr %P, i32 %P1, i32 4)]
+; CHECK: alignment assumptions should have 2 or 3 arguments
+  call void @llvm.assume(i1 true) ["align"(ptr %P, i32 %P1, i32 4, i32 4)]
+; CHECK: first argument should be a pointer
+  call void @llvm.assume(i1 true) ["align"(i32 %P1, ptr %P2)]
+; CHECK: second argument should be an integer with a maximum width of 64 bits
+  call void @llvm.assume(i1 true) ["align"(ptr %P, ptr %P2)]
+; CHECK: second argument should be an integer with a maximum width of 64 bits
+  call void @llvm.assume(i1 true) ["align"(ptr %P, i65 1)]
+; CHECK: third argument should be an integer with a maximum width of 64 bits if present
+  call void @llvm.assume(i1 true) ["align"(ptr %P, i32 %P1, ptr %P2)]
+; CHECK: third argument should be an integer with a maximum width of 64 bits if present
+  call void @llvm.assume(i1 true) ["align"(ptr %P, i32 %P1, i65 1)]
+
+; Cold checks
+;
+; CHECK: cold assumptions should have no arguments
+  call void @llvm.assume(i1 true) ["cold"(ptr %P)]
+
+; Dereferenceable checks
+;
 ; CHECK-NOT: call{{.+}}deref
   call void @llvm.assume(i1 true) ["dereferenceable"(ptr %P, i32 %P1)]
+; CHECK: first argument should be a pointer
+  call void @llvm.assume(i1 true) ["dereferenceable"(i32 %P1, i32 %P1)]
 ; CHECK: second argument should be an integer
   call void @llvm.assume(i1 true) ["dereferenceable"(ptr %P, float 1.5)]
 ; CHECK: dereferenceable assumptions should have 2 arguments
   call void @llvm.assume(i1 true) ["dereferenceable"(ptr %P, i32 8, i32 8)]
 ; CHECK: dereferenceable assumptions should have 2 arguments
   call void @llvm.assume(i1 true) ["dereferenceable"(ptr %P)]
-; CHECK: this attribute has no argument
-  call void @llvm.assume(i1 true) ["dereferenceable"(ptr %P, i32 4), "cold"(ptr %P)]
-; CHECK: this attribute should have one argument
-  call void @llvm.assume(i1 true) ["noalias"()]
-  call void @llvm.assume(i1 true) ["align"(ptr %P, i32 %P1, i32 4)]
-; CHECK: alignment assumptions should have 2 or 3 arguments
-  call void @llvm.assume(i1 true) ["align"(ptr %P, i32 %P1, i32 4, i32 4)]
+
+; DereferenceableOrNull checks
+  call void @llvm.assume(i1 true) ["dereferenceable_or_null"(ptr %P, i32 %P1)]
+; CHECK: first argument should be a pointer
+  call void @llvm.assume(i1 true) ["dereferenceable_or_null"(i32 %P1, i32 %P1)]
 ; CHECK: second argument should be an integer
-  call void @llvm.assume(i1 true) ["align"(ptr %P, ptr %P2)]
-; CHECK: third argument should be an integer if present
-  call void @llvm.assume(i1 true) ["align"(ptr %P, i32 %P1, ptr %P2)]
+  call void @llvm.assume(i1 true) ["dereferenceable_or_null"(ptr %P, float 1.5)]
+; CHECK: dereferenceable assumptions should have 2 arguments
+  call void @llvm.assume(i1 true) ["dereferenceable_or_null"(ptr %P, i32 8, i32 8)]
+; CHECK: dereferenceable assumptions should have 2 arguments
+  call void @llvm.assume(i1 true) ["dereferenceable_or_null"(ptr %P)]
+
+; NonNull checks
+;
+; CHECK: nonnull assumptions should have 1 argument
+  call void @llvm.assume(i1 true) ["nonnull"()]
+; CHECK: nonnull assumptions should have 1 argument
+  call void @llvm.assume(i1 true) ["nonnull"(ptr %P, ptr %P)]
+; CHECK: first argument should be a pointer
+  call void @llvm.assume(i1 true) ["nonnull"(i32 0)]
+
+; NoUndef checks
+;
+; CHECK: noundef assumptions should have 1 argument
+  call void @llvm.assume(i1 true) ["noundef"()]
+; CHECK: noundef assumptions should have 1 argument
+  call void @llvm.assume(i1 true) ["noundef"(ptr %P, ptr %P)]
+
+; SeparateStorage checks
+;
 ; CHECK: separate_storage assumptions should have 2 arguments
   call void @llvm.assume(i1 true) ["separate_storage"(ptr %P)]
+; CHECK: separate_storage assumptions should have 2 arguments
+  call void @llvm.assume(i1 true) ["separate_storage"(ptr %P, ptr %P, ptr %P)]
 ; CHECK: arguments to separate_storage assumptions should be pointers
   call void @llvm.assume(i1 true) ["separate_storage"(ptr %P, i32 123)]
+
+; Combining multiple attributes checks
+;
+; CHECK: cold assumptions should have no arguments
+  call void @llvm.assume(i1 true) ["dereferenceable"(ptr %P, i32 4), "cold"(ptr %P)]
 ; CHECK: dereferenceable assumptions should have 2 arguments
   call void @llvm.assume(i1 true) ["align"(ptr %P, i32 4), "dereferenceable"(ptr %P)]
-; CHECK: assume with operand bundles must have i1 true condition
-  call void @llvm.assume(i1 %cond) ["nonnull"(ptr %P)]
   ret void
 }
diff --git a/llvm/unittests/Analysis/AssumeBundleQueriesTest.cpp b/llvm/unittests/Analysis/AssumeBundleQueriesTest.cpp
index 485b70c068401..e99e2ec4930fc 100644
--- a/llvm/unittests/Analysis/AssumeBundleQueriesTest.cpp
+++ b/llvm/unittests/Analysis/AssumeBundleQueriesTest.cpp
@@ -22,10 +22,6 @@
 
 using namespace llvm;
 
-namespace llvm {
-LLVM_ABI extern cl::opt<bool> ShouldPreserveAllAttributes;
-} // namespace llvm
-
 static void RunTest(
     StringRef Head, StringRef Tail,
     std::vector<std::pair<StringRef, llvm::function_ref<void(Instruction *)>>>
@@ -126,16 +122,6 @@ TEST(AssumeQueryAPI, hasAttributeInAssume) {
         ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(1),
                                      Attribute::AttrKind::Alignment, 64));
       }));
-  Tests.push_back(std::make_pair(
-      "call void @func_many(ptr align 8 noundef %P1) cold\n", [](Instruction *I) {
-        ShouldPreserveAllAttributes.setValue(true);
-        auto *Assume = buildAssumeFromInst(I);
-        Assume->insertBefore(I->getIterator());
-        ASSERT_TRUE(hasMatchesExactlyAttributes(
-            Assume, nullptr,
-            "(align|nounwind|norecurse|noundef|willreturn|cold)"));
-        ShouldPreserveAllAttributes.setValue(false);
-      }));
   Tests.push_back(
       std::make_pair("call void @llvm.assume(i1 true)\n", [](Instruction *I) {
         auto *Assume = cast<AssumeInst>(I);
@@ -309,19 +295,6 @@ TEST(AssumeQueryAPI, fillMapFromAssume) {
         ASSERT_TRUE(MapHasRightValue(
             Map, Assume, {I->getOperand(0), Attribute::Alignment}, {64, 64}));
       }));
-  Tests.push_back(std::make_pair(
-      "call void @func_many(ptr align 8 %P1) cold\n", [](Instruction *I) {
-        ShouldPreserveAllAttributes.setValue(true);
-        auto *Assume = buildAssumeFromInst(I);
-        Assume->insertBefore(I->getIterator());
-
-        RetainedKnowledgeMap Map;
-        fillMapFromAssume(*Assume, Map);
-
-        ASSERT_TRUE(FindExactlyAttributes(
-            Map, nullptr, "(nounwind|norecurse|willreturn|cold)"));
-        ShouldPreserveAllAttributes.setValue(false);
-      }));
   Tests.push_back(
       std::make_pair("call void @llvm.assume(i1 true)\n", [](Instruction *I) {
         RetainedKnowledgeMap Map;



More information about the llvm-commits mailing list