[llvm] [IR] Reject unhandled assume bundles and seperate them from normal attributes (PR #197007)

Nikolas Klauser via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 8 05:56:40 PDT 2026


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/197007

>From 8638a3094025b99918fd28e7062ca254151b6d05 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 11 May 2026 19:09:30 +0200
Subject: [PATCH 01/13] [IR] Reject unhandled assume bundles and seperate them
 from normal attributes

---
 llvm/include/llvm/IR/BundleAttributes.def     |  18 ++
 llvm/include/llvm/IR/BundleAttributes.h       |  62 +++++
 llvm/include/llvm/IR/InstrTypes.h             |   6 +
 .../llvm/Transforms/Utils/PredicateInfo.h     |   5 +-
 llvm/lib/Analysis/LazyValueInfo.cpp           |  36 ++-
 llvm/lib/Analysis/ValueTracking.cpp           |  61 ++---
 llvm/lib/IR/BundleAttributes.cpp              |  58 +++++
 llvm/lib/IR/CMakeLists.txt                    |   1 +
 llvm/lib/IR/Verifier.cpp                      |  91 ++++----
 .../InstCombine/InstCombineCalls.cpp          |  94 ++++----
 .../Transforms/Utils/AssumeBundleBuilder.cpp  |   9 +-
 llvm/lib/Transforms/Utils/PredicateInfo.cpp   |  17 +-
 llvm/test/Transforms/Attributor/nofpclass.ll  |  28 ---
 llvm/test/Transforms/Attributor/nofree.ll     |  81 -------
 ...-split-musttail-chain-pgo-counter-promo.ll |   6 +-
 llvm/test/Transforms/InstCombine/test.ll      |  38 +++
 llvm/test/Transforms/Util/assume-builder.ll   | 217 ------------------
 llvm/test/Verifier/assume-bundles.ll          |  77 ++++++-
 .../Analysis/AssumeBundleQueriesTest.cpp      |  27 ---
 19 files changed, 415 insertions(+), 517 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
 create mode 100644 llvm/test/Transforms/InstCombine/test.ll

diff --git a/llvm/include/llvm/IR/BundleAttributes.def b/llvm/include/llvm/IR/BundleAttributes.def
new file mode 100644
index 0000000000000..2d57a0b697edc
--- /dev/null
+++ b/llvm/include/llvm/IR/BundleAttributes.def
@@ -0,0 +1,18 @@
+//===- 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)
+ATTR(NoUndef, noundef) // FIXME: Is this actually useful? It's currently exclusively emitted through tests.
+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..789e5b95fc1e4
--- /dev/null
+++ b/llvm/include/llvm/IR/BundleAttributes.h
@@ -0,0 +1,62 @@
+//===- 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 {
+  Value *Ptr;
+  Value *Alignment;
+  Value *Offset;
+};
+
+LLVM_ABI AssumeAlignInfo getAssumeAlignInfo(OperandBundleUse);
+
+struct AssumeSeparateStorageInfo {
+  const Use *Ptr1;
+  const Use *Ptr2;
+};
+
+LLVM_ABI
+AssumeSeparateStorageInfo getAssumeSeparateStorageInfo(OperandBundleUse);
+
+struct AssumeNonNullInfo {
+  Value *Ptr;
+};
+
+LLVM_ABI AssumeNonNullInfo getAssumeNonNullInfo(OperandBundleUse);
+
+struct AssumeDereferenceableInfo {
+  Value *Ptr;
+  Value *Count;
+};
+
+LLVM_ABI
+AssumeDereferenceableInfo getAssumeDereferenceableInfo(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 61dc5ebef1b1d..123a42d09ce7c 100644
--- a/llvm/include/llvm/IR/InstrTypes.h
+++ b/llvm/include/llvm/IR/InstrTypes.h
@@ -2286,6 +2286,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 b70c380dd5466..37771985bd455 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,24 @@ 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())));
-          break;
+      auto OBU = I->operand_bundles().begin()[AssumeVH.Index];
+      switch (getBundleAttrFromOBU(OBU)) {
+      case BundleAttr::NonNull:
+        assert(getAssumeNonNullInfo(OBU).Ptr == Val);
+        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())));
-          break;
+      case BundleAttr::Dereferenceable: {
+        auto [Ptr, Count] = getAssumeDereferenceableInfo(OBU);
+        assert(Ptr == Val);
+        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 91ad517f194a6..7a391182594ee 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"
@@ -829,32 +830,28 @@ 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;
+      auto OBU = I->operand_bundles().begin()[Elem.Index];
+      bool AssumeImpliesNonNull = [&]() {
+        switch (getBundleAttrFromOBU(OBU)) {
+        case BundleAttr::Dereferenceable: {
+          auto [Ptr, Count] = getAssumeDereferenceableInfo(OBU);
+          if (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 true;
 
+        default:
           return false;
-        }();
-        if (AssumeImpliesNonNull && isValidAssumeForContext(I, Q))
-          return true;
-      }
+        }
+      }();
+      if (AssumeImpliesNonNull && isValidAssumeForContext(I, Q))
+        return true;
       continue;
     }
 
@@ -1090,13 +1087,19 @@ 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));
+      auto OBU = I->operand_bundles().begin()[Elem.Index];
+      if (getBundleAttrFromOBU(OBU) == BundleAttr::Align) {
+        auto [Ptr, Alignment, Offset] = getAssumeAlignInfo(OBU);
+        assert(Ptr == V);
+        if ((Offset && !isa<ConstantInt>(Offset)) ||
+            !isa<ConstantInt>(Alignment))
+          continue;
+        auto AlignVal =
+            MinAlign(Offset ? cast<ConstantInt>(Offset)->getZExtValue() : 0,
+                     cast<ConstantInt>(Alignment)->getZExtValue());
+        if (!isPowerOf2_64(AlignVal) || !isValidAssumeForContext(I, Q))
+          continue;
+        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..19ff05a96ee27
--- /dev/null
+++ b/llvm/lib/IR/BundleAttributes.cpp
@@ -0,0 +1,58 @@
+//===- 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/ADT/StringSwitch.h"
+#include "llvm/IR/BundleAttributes.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{};
+  Ret.Ptr = OBU.Inputs[0];
+  Ret.Alignment = OBU.Inputs[1];
+  if (OBU.Inputs.size() == 3)
+    Ret.Offset = OBU.Inputs[2];
+  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 e75ba813e4559..8b5748bbf6f4e 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"
@@ -5995,56 +5996,60 @@ 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(),
+        Check(GetTypeAt(0)->isPointerTy(), "first argument should be a pointer",
+              Call);
+        Check(GetTypeAt(1)->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(OBU.Inputs.size() < 3 || GetTypeAt(2)->isIntegerTy(),
+              "third argument should be an integer 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 2a8bf3ffecd6f..3ee5d65f7f8b7 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"
@@ -3621,66 +3622,48 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
       return eraseInstFromFunction(*II);
     }
 
-    for (unsigned Idx = 0; Idx < II->getNumOperandBundles(); Idx++) {
-      OperandBundleUse OBU = II->getOperandBundleAt(Idx);
+    for (auto OBU : 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 (Offset || !isa<ConstantInt>(Alignment))
+          break;
+        auto AlignVal = cast<ConstantInt>(Alignment)->getZExtValue();
+        if (!isPowerOf2_64(AlignVal))
+          break;
 
         // Remove align 1 bundles; they don't add any useful information.
-        if (RK.ArgValue == 1)
+        if (AlignVal == 1)
           return CallBase::removeOperandBundle(II, OBU.getTagID());
 
         // 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 ((1ULL << computeKnownBits(RK.WasOn, II).countMinTrailingZeros()) <
-            RK.ArgValue)
-          continue;
+        if ((1ULL << computeKnownBits(Ptr, II).countMinTrailingZeros()) <
+            AlignVal)
+          break;
         return CallBase::removeOperandBundle(II, OBU.getTagID());
       }
 
-      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::removeOperandBundle(II, OBU.getTagID());
 
         // 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(), {});
@@ -3690,6 +3673,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/Transforms/Attributor/nofpclass.ll b/llvm/test/Transforms/Attributor/nofpclass.ll
index 788558638a5d2..7a6c9542fa055 100644
--- a/llvm/test/Transforms/Attributor/nofpclass.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass.ll
@@ -647,34 +647,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/Coroutines/coro-split-musttail-chain-pgo-counter-promo.ll b/llvm/test/Transforms/Coroutines/coro-split-musttail-chain-pgo-counter-promo.ll
index 70f15f6129d8e..f176ef59d64b5 100644
--- a/llvm/test/Transforms/Coroutines/coro-split-musttail-chain-pgo-counter-promo.ll
+++ b/llvm/test/Transforms/Coroutines/coro-split-musttail-chain-pgo-counter-promo.ll
@@ -2,14 +2,14 @@
 ; RUN: opt -passes='pgo-instr-gen,instrprof,coro-split' -do-counter-promotion=true -S < %s | FileCheck %s
 
 ; CHECK-LABEL: define internal fastcc void @f.resume
-; CHECK: musttail call fastcc void 
+; CHECK: musttail call fastcc void
 ; CHECK-NEXT: ret void
-; CHECK: musttail call fastcc void 
+; CHECK: musttail call fastcc void
 ; CHECK-NEXT: ret void
 ; CHECK-LABEL: define internal fastcc void @f.destroy
 target triple = "x86_64-grtev4-linux-gnu"
 
-%CoroutinePromise = type { ptr, i64, [8 x i8], ptr} 
+%CoroutinePromise = type { ptr, i64, [8 x i8], ptr}
 %Awaitable.1 = type { ptr }
 %Awaitable.2 = type { ptr, ptr }
 
diff --git a/llvm/test/Transforms/InstCombine/test.ll b/llvm/test/Transforms/InstCombine/test.ll
new file mode 100644
index 0000000000000..c5b859e19f486
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/test.ll
@@ -0,0 +1,38 @@
+
+define void @f2(ptr %a) {
+; CHECK-LABEL: @f2(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[A:%.*]], i64 32, i32 24) ]
+; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 8
+; CHECK-NEXT:    [[TMP1:%.*]] = ptrtoint ptr [[TMP0]] to i64
+; CHECK-NEXT:    [[TMP2:%.*]] = and i64 [[TMP1]], 8
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i64 [[TMP2]], 0
+; CHECK-NEXT:    br i1 [[TMP3]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
+; CHECK:       if.then:
+; CHECK-NEXT:    store i64 16, ptr [[TMP0]], align 4
+; CHECK-NEXT:    br label [[IF_END:%.*]]
+; CHECK:       if.else:
+; CHECK-NEXT:    store i8 1, ptr [[TMP0]], align 1
+; CHECK-NEXT:    br label [[IF_END]]
+; CHECK:       if.end:
+; CHECK-NEXT:    ret void
+;
+entry:
+  call void @llvm.assume(i1 true) [ "align"(ptr %a, i64 32, i32 24) ]
+  %0 = getelementptr inbounds i8, ptr %a, i64 8
+  %1 = ptrtoint ptr %0 to i64
+  %2 = and i64 %1, 15
+  %3 = icmp eq i64 %2, 0
+  br i1 %3, label %if.then, label %if.else
+
+if.then:                                          ; preds = %entry
+  store i64 16, ptr %0, align 4
+  br label %if.end
+
+if.else:                                          ; preds = %entry
+  store i8 1, ptr %0, align 1
+  br label %if.end
+
+if.end:                                           ; preds = %if.else, %if.then
+  ret void
+}
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..18d04b978d013 100644
--- a/llvm/test/Verifier/assume-bundles.ll
+++ b/llvm/test/Verifier/assume-bundles.ll
@@ -7,32 +7,85 @@ 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
+  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)]
+
+; 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;

>From fa8cc24c38a97a90be4913ee87f35cf2a4b72157 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 11 May 2026 23:21:34 +0200
Subject: [PATCH 02/13] Address comments

---
 llvm/include/llvm/IR/BundleAttributes.h       |  4 +-
 llvm/lib/Analysis/ValueTracking.cpp           | 12 ++----
 llvm/lib/IR/BundleAttributes.cpp              | 11 ++++--
 .../InstCombine/InstCombineCalls.cpp          |  9 ++---
 llvm/test/Transforms/InstCombine/test.ll      | 38 -------------------
 5 files changed, 16 insertions(+), 58 deletions(-)
 delete mode 100644 llvm/test/Transforms/InstCombine/test.ll

diff --git a/llvm/include/llvm/IR/BundleAttributes.h b/llvm/include/llvm/IR/BundleAttributes.h
index 789e5b95fc1e4..91c2d58c63d82 100644
--- a/llvm/include/llvm/IR/BundleAttributes.h
+++ b/llvm/include/llvm/IR/BundleAttributes.h
@@ -29,8 +29,8 @@ inline BundleAttr getBundleAttrFromOBU(OperandBundleUse OBU) {
 
 struct AssumeAlignInfo {
   Value *Ptr;
-  Value *Alignment;
-  Value *Offset;
+  std::optional<uint64_t> Alignment;
+  std::optional<uint64_t> Offset;
 };
 
 LLVM_ABI AssumeAlignInfo getAssumeAlignInfo(OperandBundleUse);
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 7a391182594ee..1f46a80e491d6 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -1091,15 +1091,11 @@ void llvm::computeKnownBitsFromContext(const Value *V, KnownBits &Known,
       if (getBundleAttrFromOBU(OBU) == BundleAttr::Align) {
         auto [Ptr, Alignment, Offset] = getAssumeAlignInfo(OBU);
         assert(Ptr == V);
-        if ((Offset && !isa<ConstantInt>(Offset)) ||
-            !isa<ConstantInt>(Alignment))
+        if (!Alignment || !isPowerOf2_64(*Alignment))
           continue;
-        auto AlignVal =
-            MinAlign(Offset ? cast<ConstantInt>(Offset)->getZExtValue() : 0,
-                     cast<ConstantInt>(Alignment)->getZExtValue());
-        if (!isPowerOf2_64(AlignVal) || !isValidAssumeForContext(I, Q))
-          continue;
-        Known.Zero.setLowBits(Log2_64(AlignVal));
+        auto AlignVal = MinAlign(Offset.value_or(0), *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
index 19ff05a96ee27..e9320efbd0adc 100644
--- a/llvm/lib/IR/BundleAttributes.cpp
+++ b/llvm/lib/IR/BundleAttributes.cpp
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "llvm/ADT/StringSwitch.h"
 #include "llvm/IR/BundleAttributes.h"
+#include "llvm/ADT/StringSwitch.h"
 
 using namespace llvm;
 
@@ -34,9 +34,12 @@ AssumeAlignInfo llvm::getAssumeAlignInfo(OperandBundleUse OBU) {
          OBU.Inputs.size() <= 3);
   AssumeAlignInfo Ret{};
   Ret.Ptr = OBU.Inputs[0];
-  Ret.Alignment = OBU.Inputs[1];
-  if (OBU.Inputs.size() == 3)
-    Ret.Offset = OBU.Inputs[2];
+  if (auto *Align = dyn_cast<ConstantInt>(OBU.Inputs[1]))
+    Ret.Alignment = Align->getZExtValue();
+  if (OBU.Inputs.size() == 3) {
+    if (auto *Offset = dyn_cast<ConstantInt>(OBU.Inputs[2]))
+      Ret.Offset = Offset->getZExtValue();
+  }
   return Ret;
 }
 
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 3ee5d65f7f8b7..bd5388e2a090f 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -3630,14 +3630,11 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
         // Try to remove redundant alignment assumptions.
         auto [Ptr, Alignment, Offset] = getAssumeAlignInfo(OBU);
 
-        if (Offset || !isa<ConstantInt>(Alignment))
-          break;
-        auto AlignVal = cast<ConstantInt>(Alignment)->getZExtValue();
-        if (!isPowerOf2_64(AlignVal))
+        if (Offset || !Alignment || !isPowerOf2_64(*Alignment))
           break;
 
         // Remove align 1 bundles; they don't add any useful information.
-        if (AlignVal == 1)
+        if (*Alignment == 1)
           return CallBase::removeOperandBundle(II, OBU.getTagID());
 
         // Don't try to remove align assumptions for pointers derived from
@@ -3650,7 +3647,7 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
         // Compute known bits for the pointer and drop the assume if the
         // known alignment isn't increased by it.
         if ((1ULL << computeKnownBits(Ptr, II).countMinTrailingZeros()) <
-            AlignVal)
+            *Alignment)
           break;
         return CallBase::removeOperandBundle(II, OBU.getTagID());
       }
diff --git a/llvm/test/Transforms/InstCombine/test.ll b/llvm/test/Transforms/InstCombine/test.ll
deleted file mode 100644
index c5b859e19f486..0000000000000
--- a/llvm/test/Transforms/InstCombine/test.ll
+++ /dev/null
@@ -1,38 +0,0 @@
-
-define void @f2(ptr %a) {
-; CHECK-LABEL: @f2(
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[A:%.*]], i64 32, i32 24) ]
-; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 8
-; CHECK-NEXT:    [[TMP1:%.*]] = ptrtoint ptr [[TMP0]] to i64
-; CHECK-NEXT:    [[TMP2:%.*]] = and i64 [[TMP1]], 8
-; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i64 [[TMP2]], 0
-; CHECK-NEXT:    br i1 [[TMP3]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
-; CHECK:       if.then:
-; CHECK-NEXT:    store i64 16, ptr [[TMP0]], align 4
-; CHECK-NEXT:    br label [[IF_END:%.*]]
-; CHECK:       if.else:
-; CHECK-NEXT:    store i8 1, ptr [[TMP0]], align 1
-; CHECK-NEXT:    br label [[IF_END]]
-; CHECK:       if.end:
-; CHECK-NEXT:    ret void
-;
-entry:
-  call void @llvm.assume(i1 true) [ "align"(ptr %a, i64 32, i32 24) ]
-  %0 = getelementptr inbounds i8, ptr %a, i64 8
-  %1 = ptrtoint ptr %0 to i64
-  %2 = and i64 %1, 15
-  %3 = icmp eq i64 %2, 0
-  br i1 %3, label %if.then, label %if.else
-
-if.then:                                          ; preds = %entry
-  store i64 16, ptr %0, align 4
-  br label %if.end
-
-if.else:                                          ; preds = %entry
-  store i8 1, ptr %0, align 1
-  br label %if.end
-
-if.end:                                           ; preds = %if.else, %if.then
-  ret void
-}

>From e62c100ea37c5e595e3e0b525007bb82913724a0 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 12 May 2026 13:38:30 +0200
Subject: [PATCH 03/13] Address comments

---
 llvm/lib/Analysis/LazyValueInfo.cpp                         | 4 ++--
 llvm/lib/Analysis/ValueTracking.cpp                         | 6 +++---
 .../coro-split-musttail-chain-pgo-counter-promo.ll          | 6 +++---
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp
index 37771985bd455..593c2cd0e9dad 100644
--- a/llvm/lib/Analysis/LazyValueInfo.cpp
+++ b/llvm/lib/Analysis/LazyValueInfo.cpp
@@ -856,8 +856,8 @@ void LazyValueInfoImpl::intersectAssumeOrGuardBlockValueConstantRange(
       continue;
 
     if (AssumeVH.Index != AssumptionCache::ExprResultIdx) {
-      auto OBU = I->operand_bundles().begin()[AssumeVH.Index];
-      switch (getBundleAttrFromOBU(OBU)) {
+      switch (auto OBU = I->getOperandBundleAt(AssumeVH.Index);
+              getBundleAttrFromOBU(OBU)) {
       case BundleAttr::NonNull:
         assert(getAssumeNonNullInfo(OBU).Ptr == Val);
         BBLV = BBLV.intersect(ValueLatticeElement::getNot(
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 1f46a80e491d6..d8ca20159c013 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -830,8 +830,8 @@ static bool isKnownNonZeroFromAssume(const Value *V, const SimplifyQuery &Q) {
            "Got assumption for the wrong function!");
 
     if (Elem.Index != AssumptionCache::ExprResultIdx) {
-      auto OBU = I->operand_bundles().begin()[Elem.Index];
       bool AssumeImpliesNonNull = [&]() {
+        auto OBU = I->getOperandBundleAt(Elem.Index);
         switch (getBundleAttrFromOBU(OBU)) {
         case BundleAttr::Dereferenceable: {
           auto [Ptr, Count] = getAssumeDereferenceableInfo(OBU);
@@ -1087,8 +1087,8 @@ void llvm::computeKnownBitsFromContext(const Value *V, KnownBits &Known,
            "Got assumption for the wrong function!");
 
     if (Elem.Index != AssumptionCache::ExprResultIdx) {
-      auto OBU = I->operand_bundles().begin()[Elem.Index];
-      if (getBundleAttrFromOBU(OBU) == BundleAttr::Align) {
+      if (auto OBU = I->getOperandBundleAt(Elem.Index);
+          getBundleAttrFromOBU(OBU) == BundleAttr::Align) {
         auto [Ptr, Alignment, Offset] = getAssumeAlignInfo(OBU);
         assert(Ptr == V);
         if (!Alignment || !isPowerOf2_64(*Alignment))
diff --git a/llvm/test/Transforms/Coroutines/coro-split-musttail-chain-pgo-counter-promo.ll b/llvm/test/Transforms/Coroutines/coro-split-musttail-chain-pgo-counter-promo.ll
index f176ef59d64b5..70f15f6129d8e 100644
--- a/llvm/test/Transforms/Coroutines/coro-split-musttail-chain-pgo-counter-promo.ll
+++ b/llvm/test/Transforms/Coroutines/coro-split-musttail-chain-pgo-counter-promo.ll
@@ -2,14 +2,14 @@
 ; RUN: opt -passes='pgo-instr-gen,instrprof,coro-split' -do-counter-promotion=true -S < %s | FileCheck %s
 
 ; CHECK-LABEL: define internal fastcc void @f.resume
-; CHECK: musttail call fastcc void
+; CHECK: musttail call fastcc void 
 ; CHECK-NEXT: ret void
-; CHECK: musttail call fastcc void
+; CHECK: musttail call fastcc void 
 ; CHECK-NEXT: ret void
 ; CHECK-LABEL: define internal fastcc void @f.destroy
 target triple = "x86_64-grtev4-linux-gnu"
 
-%CoroutinePromise = type { ptr, i64, [8 x i8], ptr}
+%CoroutinePromise = type { ptr, i64, [8 x i8], ptr} 
 %Awaitable.1 = type { ptr }
 %Awaitable.2 = type { ptr, ptr }
 

>From 4ec7fa0bc251b4816425601b272fc1746e773d0e Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 12 May 2026 17:35:05 +0200
Subject: [PATCH 04/13] CI fux

---
 llvm/lib/IR/BundleAttributes.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/llvm/lib/IR/BundleAttributes.cpp b/llvm/lib/IR/BundleAttributes.cpp
index e9320efbd0adc..e9b4077033a99 100644
--- a/llvm/lib/IR/BundleAttributes.cpp
+++ b/llvm/lib/IR/BundleAttributes.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/IR/BundleAttributes.h"
+#include "llvm/IR/Constants.h"
 #include "llvm/ADT/StringSwitch.h"
 
 using namespace llvm;

>From e7fc5cc5cddc7f957ba51e86ddc5bc92d9807d13 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Wed, 13 May 2026 17:06:19 +0200
Subject: [PATCH 05/13] Fix CI

---
 .../ValueTracking/assume-queries-counter.ll   | 34 ++++++++-----------
 1 file changed, 14 insertions(+), 20 deletions(-)

diff --git a/llvm/test/Analysis/ValueTracking/assume-queries-counter.ll b/llvm/test/Analysis/ValueTracking/assume-queries-counter.ll
index 0cb8279f3db38..24b0cc923106a 100644
--- a/llvm/test/Analysis/ValueTracking/assume-queries-counter.ll
+++ b/llvm/test/Analysis/ValueTracking/assume-queries-counter.ll
@@ -14,13 +14,11 @@ define dso_local i1 @test1(ptr readonly %0) {
 ;
 ; 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]]
+; COUNTER2-NEXT:    ret i1 false
 ;
 ; 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]]
+; COUNTER3-NEXT:    ret i1 false
 ;
   call void @llvm.assume(i1 true) ["nonnull"(ptr %0)]
   %2 = icmp eq ptr %0, null
@@ -29,18 +27,16 @@ define dso_local i1 @test1(ptr readonly %0) {
 
 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]]
+; COUNTER1-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[TMP0:%.*]]) ]
+; COUNTER1-NEXT:    ret i1 false
 ;
 ; 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]]
+; COUNTER3-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[TMP0:%.*]]) ]
+; COUNTER3-NEXT:    ret i1 false
 ;
   %2 = icmp eq ptr %0, null
   call void @llvm.assume(i1 true) ["nonnull"(ptr %0)]
@@ -54,13 +50,12 @@ define dso_local i32 @test4(ptr readonly %0, i1 %cond) nofree nosync {
 ; 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:    br i1 false, label [[TMP5:%.*]], label [[TMP2:%.*]]
+; COUNTER1:       2:
 ; 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:       4:
+; COUNTER1-NEXT:    [[TMP6:%.*]] = phi i32 [ [[TMP4]], [[TMP2]] ], [ poison, [[A]] ]
 ; COUNTER1-NEXT:    ret i32 [[TMP6]]
 ;
 ; COUNTER2-LABEL: @test4(
@@ -69,13 +64,12 @@ define dso_local i32 @test4(ptr readonly %0, i1 %cond) nofree nosync {
 ; 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:    br i1 false, label [[TMP5:%.*]], label [[TMP2:%.*]]
+; COUNTER2:       2:
 ; 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:       4:
+; COUNTER2-NEXT:    [[TMP6:%.*]] = phi i32 [ [[TMP4]], [[TMP2]] ], [ poison, [[A]] ]
 ; COUNTER2-NEXT:    ret i32 [[TMP6]]
 ;
 ; COUNTER3-LABEL: @test4(

>From 8a8dfeebe006046ff9e88fe37d59e5bde4874fe1 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 15 May 2026 10:20:45 +0200
Subject: [PATCH 06/13] Fix formatting

---
 llvm/lib/IR/BundleAttributes.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/IR/BundleAttributes.cpp b/llvm/lib/IR/BundleAttributes.cpp
index e9b4077033a99..aee4495d9d822 100644
--- a/llvm/lib/IR/BundleAttributes.cpp
+++ b/llvm/lib/IR/BundleAttributes.cpp
@@ -7,8 +7,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/IR/BundleAttributes.h"
-#include "llvm/IR/Constants.h"
+
 #include "llvm/ADT/StringSwitch.h"
+#include "llvm/IR/Constants.h"
 
 using namespace llvm;
 

>From cddd3a22b389a9f1c589de11b3428c5776384c01 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sun, 17 May 2026 14:52:25 +0200
Subject: [PATCH 07/13] Expose uses

---
 llvm/include/llvm/IR/BundleAttributes.h       | 23 ++++++++++---------
 llvm/lib/Analysis/ValueTracking.cpp           |  2 +-
 llvm/lib/IR/BundleAttributes.cpp              |  9 ++++----
 .../InstCombine/InstCombineCalls.cpp          |  6 ++---
 4 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/llvm/include/llvm/IR/BundleAttributes.h b/llvm/include/llvm/IR/BundleAttributes.h
index 91c2d58c63d82..87bc4afbae904 100644
--- a/llvm/include/llvm/IR/BundleAttributes.h
+++ b/llvm/include/llvm/IR/BundleAttributes.h
@@ -28,20 +28,21 @@ inline BundleAttr getBundleAttrFromOBU(OperandBundleUse OBU) {
 }
 
 struct AssumeAlignInfo {
-  Value *Ptr;
-  std::optional<uint64_t> Alignment;
-  std::optional<uint64_t> Offset;
+  const Use &Ptr;
+  const Use &Alignment;
+  std::optional<uint64_t> AlignmentVal;
+  std::optional<uint64_t> OffsetVal;
 };
 
 LLVM_ABI AssumeAlignInfo getAssumeAlignInfo(OperandBundleUse);
 
-struct AssumeSeparateStorageInfo {
-  const Use *Ptr1;
-  const Use *Ptr2;
+struct AssumeDereferenceableInfo {
+  const Use &Ptr;
+  const Use &Count;
 };
 
 LLVM_ABI
-AssumeSeparateStorageInfo getAssumeSeparateStorageInfo(OperandBundleUse);
+AssumeDereferenceableInfo getAssumeDereferenceableInfo(OperandBundleUse);
 
 struct AssumeNonNullInfo {
   Value *Ptr;
@@ -49,13 +50,13 @@ struct AssumeNonNullInfo {
 
 LLVM_ABI AssumeNonNullInfo getAssumeNonNullInfo(OperandBundleUse);
 
-struct AssumeDereferenceableInfo {
-  Value *Ptr;
-  Value *Count;
+struct AssumeSeparateStorageInfo {
+  const Use &Ptr1;
+  const Use &Ptr2;
 };
 
 LLVM_ABI
-AssumeDereferenceableInfo getAssumeDereferenceableInfo(OperandBundleUse);
+AssumeSeparateStorageInfo getAssumeSeparateStorageInfo(OperandBundleUse);
 
 } // namespace llvm
 
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 59500889555dc..6f78e3fb0ea54 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -1089,7 +1089,7 @@ void llvm::computeKnownBitsFromContext(const Value *V, KnownBits &Known,
     if (Elem.Index != AssumptionCache::ExprResultIdx) {
       if (auto OBU = I->getOperandBundleAt(Elem.Index);
           getBundleAttrFromOBU(OBU) == BundleAttr::Align) {
-        auto [Ptr, Alignment, Offset] = getAssumeAlignInfo(OBU);
+        auto [Ptr, _, Alignment, Offset] = getAssumeAlignInfo(OBU);
         assert(Ptr == V);
         if (!Alignment || !isPowerOf2_64(*Alignment))
           continue;
diff --git a/llvm/lib/IR/BundleAttributes.cpp b/llvm/lib/IR/BundleAttributes.cpp
index aee4495d9d822..e64e7b55df3bf 100644
--- a/llvm/lib/IR/BundleAttributes.cpp
+++ b/llvm/lib/IR/BundleAttributes.cpp
@@ -34,13 +34,12 @@ BundleAttr llvm::getBundleAttrFromString(StringRef Str) {
 AssumeAlignInfo llvm::getAssumeAlignInfo(OperandBundleUse OBU) {
   assert(OBU.getTagName() == "align" && OBU.Inputs.size() >= 2 &&
          OBU.Inputs.size() <= 3);
-  AssumeAlignInfo Ret{};
-  Ret.Ptr = OBU.Inputs[0];
+  AssumeAlignInfo Ret{OBU.Inputs[0], OBU.Inputs[1], std::nullopt, std::nullopt};
   if (auto *Align = dyn_cast<ConstantInt>(OBU.Inputs[1]))
-    Ret.Alignment = Align->getZExtValue();
+    Ret.AlignmentVal = Align->getZExtValue();
   if (OBU.Inputs.size() == 3) {
     if (auto *Offset = dyn_cast<ConstantInt>(OBU.Inputs[2]))
-      Ret.Offset = Offset->getZExtValue();
+      Ret.OffsetVal = Offset->getZExtValue();
   }
   return Ret;
 }
@@ -48,7 +47,7 @@ AssumeAlignInfo llvm::getAssumeAlignInfo(OperandBundleUse OBU) {
 AssumeSeparateStorageInfo
 llvm::getAssumeSeparateStorageInfo(OperandBundleUse OBU) {
   assert(OBU.getTagName() == "separate_storage" && OBU.Inputs.size() == 2);
-  return {&OBU.Inputs[0], &OBU.Inputs[1]};
+  return {OBU.Inputs[0], OBU.Inputs[1]};
 }
 
 AssumeNonNullInfo llvm::getAssumeNonNullInfo(OperandBundleUse OBU) {
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index bd5388e2a090f..465fde2c66442 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -3628,7 +3628,7 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
         llvm_unreachable("Unexpected Attribute");
       case BundleAttr::Align: {
         // Try to remove redundant alignment assumptions.
-        auto [Ptr, Alignment, Offset] = getAssumeAlignInfo(OBU);
+        auto [Ptr, _, Alignment, Offset] = getAssumeAlignInfo(OBU);
 
         if (Offset || !Alignment || !isPowerOf2_64(*Alignment))
           break;
@@ -3687,8 +3687,8 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
           if (Hint != UnderlyingObject)
             replaceUse(const_cast<Use &>(U), UnderlyingObject);
         };
-        MaybeSimplifyHint(*Ptr1);
-        MaybeSimplifyHint(*Ptr2);
+        MaybeSimplifyHint(Ptr1);
+        MaybeSimplifyHint(Ptr2);
       } break;
 
       // TODO: Drop these assumes when they are redundant

>From 4615c8559091010ab00e7c37ac773e782049f870 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 29 May 2026 10:14:03 +0200
Subject: [PATCH 08/13] Address comments

---
 llvm/docs/LangRef.rst               | 14 ++++++++++++--
 llvm/docs/ReleaseNotes.md           |  7 ++++++-
 llvm/lib/Analysis/ValueTracking.cpp |  2 ++
 3 files changed, 20 insertions(+), 3 deletions(-)

diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 74c90d1f06532..4924b61f08d05 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -3120,6 +3120,16 @@ assumptions, such as that a :ref:`parameter attribute <paramattrs>` or a
 location. Operand bundles enable assumptions that are either hard or impossible
 to represent as a boolean argument of an :ref:`llvm.assume <int_assume>`.
 
+The following attributes are currently accepted:
+* ``align``
+* ``cold``
+* ``dereferenceable``
+* ``dereferenceable_or_null``
+* ``ignore``
+* ``nonnull``
+* ``noundef``
+* ``separate_storage``
+
 Assumes with operand bundles must have ``i1 true`` as the condition operand.
 
 An assume operand bundle has the form:
@@ -8055,7 +8065,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
@@ -8684,7 +8694,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 013b5d0a52067..190202692293b 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``.
+
 ### Changes to LLVM infrastructure
 
 * Removed ``Constant::isZeroValue``. It was functionally identical to
@@ -130,7 +135,7 @@ Makes programs 10x faster by doing Special New Thing.
   ([#177046](https://github.com/llvm/llvm-project/pull/125687)). Note that
   there are still issues with invalid compiler reasoning about some functions
   in bitcode, e.g. `malloc`. Not yet supported on MachO or when using
-  distributed ThinLTO. 
+  distributed ThinLTO.
 
 * ``ConstantFP`` now supports vector types and is the canonical form returned by
   ``ConstantVector::getSplat(C)`` when ``C`` is a scalar ``ConstantFP``.
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 6f78e3fb0ea54..e6871e0f7fc32 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -835,6 +835,7 @@ static bool isKnownNonZeroFromAssume(const Value *V, const SimplifyQuery &Q) {
         switch (getBundleAttrFromOBU(OBU)) {
         case BundleAttr::Dereferenceable: {
           auto [Ptr, Count] = getAssumeDereferenceableInfo(OBU);
+          assert(Ptr == V);
           if (NullPointerIsDefined(Q.CxtI->getFunction(),
                                    V->getType()->getPointerAddressSpace()))
             return false;
@@ -844,6 +845,7 @@ static bool isKnownNonZeroFromAssume(const Value *V, const SimplifyQuery &Q) {
         }
 
         case BundleAttr::NonNull:
+          assert(getAssumeNonNullInfo(OBU).Ptr == V);
           return true;
 
         default:

>From 6d7647b250f7c0c198d4113dfeafbad55a75b09f Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 4 Jun 2026 14:17:37 +0200
Subject: [PATCH 09/13] Address comments

---
 llvm/docs/LangRef.rst                         | 116 ++++++++----------
 llvm/include/llvm/IR/BundleAttributes.def     |   6 +-
 llvm/include/llvm/IR/BundleAttributes.h       |   2 +-
 llvm/lib/Analysis/LazyValueInfo.cpp           |   4 +-
 llvm/lib/Analysis/ValueTracking.cpp           |   4 +-
 llvm/lib/IR/BundleAttributes.cpp              |   2 +
 .../InstCombine/InstCombineCalls.cpp          |   2 +-
 .../ValueTracking/assume-queries-counter.ll   | 106 ----------------
 llvm/test/Analysis/ValueTracking/assume.ll    |  66 ++++++++++
 llvm/test/Transforms/InstCombine/assume.ll    |  71 +++++++++++
 10 files changed, 203 insertions(+), 176 deletions(-)
 delete mode 100644 llvm/test/Analysis/ValueTracking/assume-queries-counter.ll

diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 342394d12d8a9..49b5234b6915f 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,43 +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>`.
-
-The following attributes are currently accepted:
-* ``align``
-* ``cold``
-* ``dereferenceable``
-* ``dereferenceable_or_null``
-* ``ignore``
-* ``nonnull``
-* ``noundef``
-* ``separate_storage``
+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:
 
-::
+.. code-block:: llvm
 
-      "<tag>"([ <arguments>] ])
+      call void @llvm.assume(i1 true) ["align"(ptr %val, i32 %align)]
 
-In the case of function or parameter attributes, the operand bundle has the
-restricted form:
+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 attribute doesn't
+  imply ``nonnull``.
 
-      "<tag>"([ <holds for value> [, <attribute argument>] ])
+``"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.
 
-* 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.
+``"ignore"(...)``
+  Doesn't imply anything and is ignored. This is used to drop an assume where
+  modifying the ``llvm.assume`` call cannot be replaced or dropped.
 
-If there are no arguments the attribute is a property of the call location.
+``"nonnull"(ptr %p)``
+  Equivalent to :ref:`nonnull <attr_nonnull>` on ``%p``.
+
+``"noundef"(any_type %v)``
+  Equivalent to :ref:`noundef <attr_noundef>` on ``%v``.
+
+``"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:
 
@@ -3181,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:
diff --git a/llvm/include/llvm/IR/BundleAttributes.def b/llvm/include/llvm/IR/BundleAttributes.def
index 2d57a0b697edc..78c692393fb44 100644
--- a/llvm/include/llvm/IR/BundleAttributes.def
+++ b/llvm/include/llvm/IR/BundleAttributes.def
@@ -12,7 +12,11 @@ ATTR(Dereferenceable, dereferenceable)
 ATTR(DereferenceableOrNull, dereferenceable_or_null)
 ATTR(Ignore, ignore)
 ATTR(NonNull, nonnull)
-ATTR(NoUndef, noundef) // FIXME: Is this actually useful? It's currently exclusively emitted through tests.
+
+// 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
index 87bc4afbae904..ee6e5b4bc1ef6 100644
--- a/llvm/include/llvm/IR/BundleAttributes.h
+++ b/llvm/include/llvm/IR/BundleAttributes.h
@@ -45,7 +45,7 @@ LLVM_ABI
 AssumeDereferenceableInfo getAssumeDereferenceableInfo(OperandBundleUse);
 
 struct AssumeNonNullInfo {
-  Value *Ptr;
+  const Use &Ptr;
 };
 
 LLVM_ABI AssumeNonNullInfo getAssumeNonNullInfo(OperandBundleUse);
diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp
index d50720bab4d16..3a875bd680fb9 100644
--- a/llvm/lib/Analysis/LazyValueInfo.cpp
+++ b/llvm/lib/Analysis/LazyValueInfo.cpp
@@ -856,8 +856,8 @@ void LazyValueInfoImpl::intersectAssumeOrGuardBlockValueConstantRange(
       continue;
 
     if (AssumeVH.Index != AssumptionCache::ExprResultIdx) {
-      switch (auto OBU = I->getOperandBundleAt(AssumeVH.Index);
-              getBundleAttrFromOBU(OBU)) {
+      auto OBU = I->getOperandBundleAt(AssumeVH.Index);
+      switch (getBundleAttrFromOBU(OBU)) {
       case BundleAttr::NonNull:
         assert(getAssumeNonNullInfo(OBU).Ptr == Val);
         BBLV = BBLV.intersect(ValueLatticeElement::getNot(
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 4a78f2fb4bb63..f46b4e9c94b7d 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -1091,9 +1091,9 @@ void llvm::computeKnownBitsFromContext(const Value *V, KnownBits &Known,
           getBundleAttrFromOBU(OBU) == BundleAttr::Align) {
         auto [Ptr, _, Alignment, Offset] = getAssumeAlignInfo(OBU);
         assert(Ptr == V);
-        if (!Alignment || !isPowerOf2_64(*Alignment))
+        if (!Alignment || !Offset || !isPowerOf2_64(*Alignment))
           continue;
-        auto AlignVal = MinAlign(Offset.value_or(0), *Alignment);
+        auto AlignVal = MinAlign(*Offset, *Alignment);
         if (isValidAssumeForContext(I, Q))
           Known.Zero.setLowBits(Log2_64(AlignVal));
       }
diff --git a/llvm/lib/IR/BundleAttributes.cpp b/llvm/lib/IR/BundleAttributes.cpp
index e64e7b55df3bf..3e4e65e8fd3d1 100644
--- a/llvm/lib/IR/BundleAttributes.cpp
+++ b/llvm/lib/IR/BundleAttributes.cpp
@@ -40,6 +40,8 @@ AssumeAlignInfo llvm::getAssumeAlignInfo(OperandBundleUse OBU) {
   if (OBU.Inputs.size() == 3) {
     if (auto *Offset = dyn_cast<ConstantInt>(OBU.Inputs[2]))
       Ret.OffsetVal = Offset->getZExtValue();
+  } else {
+    Ret.OffsetVal = 0;
   }
   return Ret;
 }
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 768227d682765..92cb4ac2af23f 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -3656,7 +3656,7 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
         // Try to remove redundant alignment assumptions.
         auto [Ptr, _, Alignment, Offset] = getAssumeAlignInfo(OBU);
 
-        if (Offset || !Alignment || !isPowerOf2_64(*Alignment))
+        if (!Alignment || !Offset || *Offset != 0 || !isPowerOf2_64(*Alignment))
           break;
 
         // Remove align 1 bundles; they don't add any useful information.
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 24b0cc923106a..0000000000000
--- a/llvm/test/Analysis/ValueTracking/assume-queries-counter.ll
+++ /dev/null
@@ -1,106 +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:    ret i1 false
-;
-; COUNTER3-LABEL: @test1(
-; COUNTER3-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[TMP0:%.*]]) ]
-; COUNTER3-NEXT:    ret i1 false
-;
-  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:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[TMP0:%.*]]) ]
-; COUNTER1-NEXT:    ret i1 false
-;
-; COUNTER2-LABEL: @test2(
-; COUNTER2-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[TMP0:%.*]]) ]
-; COUNTER2-NEXT:    ret i1 false
-;
-; COUNTER3-LABEL: @test2(
-; COUNTER3-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[TMP0:%.*]]) ]
-; COUNTER3-NEXT:    ret i1 false
-;
-  %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:    br i1 false, label [[TMP5:%.*]], label [[TMP2:%.*]]
-; COUNTER1:       2:
-; COUNTER1-NEXT:    [[TMP4:%.*]] = load i32, ptr [[TMP0]], align 4
-; COUNTER1-NEXT:    br label [[TMP5]]
-; COUNTER1:       4:
-; COUNTER1-NEXT:    [[TMP6:%.*]] = phi i32 [ [[TMP4]], [[TMP2]] ], [ poison, [[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:    br i1 false, label [[TMP5:%.*]], label [[TMP2:%.*]]
-; COUNTER2:       2:
-; COUNTER2-NEXT:    [[TMP4:%.*]] = load i32, ptr [[TMP0]], align 4
-; COUNTER2-NEXT:    br label [[TMP5]]
-; COUNTER2:       4:
-; COUNTER2-NEXT:    [[TMP6:%.*]] = phi i32 [ [[TMP4]], [[TMP2]] ], [ poison, [[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..fa5a03ba997c4 100644
--- a/llvm/test/Analysis/ValueTracking/assume.ll
+++ b/llvm/test/Analysis/ValueTracking/assume.ll
@@ -191,3 +191,69 @@ 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:    ret i1 true
+;
+  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/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()

>From 418b809d38200244d5d5b9586a73747c44cfb3f9 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 4 Jun 2026 15:53:33 +0200
Subject: [PATCH 10/13] Fix test

---
 llvm/test/Analysis/ValueTracking/assume.ll | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/llvm/test/Analysis/ValueTracking/assume.ll b/llvm/test/Analysis/ValueTracking/assume.ll
index fa5a03ba997c4..c5fe253634716 100644
--- a/llvm/test/Analysis/ValueTracking/assume.ll
+++ b/llvm/test/Analysis/ValueTracking/assume.ll
@@ -249,7 +249,10 @@ define i1 @test_align_with_constant_offset_8(ptr %ptr) {
 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:    ret i1 true
+; 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

>From 7f6307b35cd72571dc664778604cc6fbc4c58a82 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 4 Jun 2026 16:35:29 +0200
Subject: [PATCH 11/13] Reject alignment and offsets with more than 64 bits

---
 llvm/lib/IR/Verifier.cpp             | 15 +++++++++++----
 llvm/test/Verifier/assume-bundles.ll |  8 ++++++--
 2 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index cedf477472451..4420a090a146d 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -6120,10 +6120,17 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
               "alignment assumptions should have 2 or 3 arguments", Call);
         Check(GetTypeAt(0)->isPointerTy(), "first argument should be a pointer",
               Call);
-        Check(GetTypeAt(1)->isIntegerTy(),
-              "second argument should be an integer", Call);
-        Check(OBU.Inputs.size() < 3 || GetTypeAt(2)->isIntegerTy(),
-              "third argument should be an integer if present", 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,
diff --git a/llvm/test/Verifier/assume-bundles.ll b/llvm/test/Verifier/assume-bundles.ll
index 18d04b978d013..efba13dd7c7c5 100644
--- a/llvm/test/Verifier/assume-bundles.ll
+++ b/llvm/test/Verifier/assume-bundles.ll
@@ -22,10 +22,14 @@ define void @func(ptr %P, i32 %P1, ptr %P2, ptr %P3, i1 %cond) {
   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
+; 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: third argument should be an integer if present
+; 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
 ;

>From 9d8ed4b7628ec329ec4a3eefb0a266abd82fe2ef Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 4 Jun 2026 17:04:23 +0200
Subject: [PATCH 12/13] Fix test

---
 llvm/test/Transforms/AlignmentFromAssumptions/simple.ll | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

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
-

>From cd89420d8ab8f1a1b0c40da75bdc471158408bfb Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 8 Jun 2026 14:56:19 +0200
Subject: [PATCH 13/13] Address comments

---
 llvm/docs/LangRef.rst | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index fc07d4a16c3ec..ca1f007865c03 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -3166,7 +3166,7 @@ The following attributes are currently accepted:
 
 ``"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 attribute doesn't
+  except that ``%size`` may also be zero, in which case the bundle doesn't
   imply ``nonnull``.
 
 ``"dereferenceable_or_null"(ptr %p, i64 %size)``
@@ -3176,7 +3176,7 @@ The following attributes are currently accepted:
 
 ``"ignore"(...)``
   Doesn't imply anything and is ignored. This is used to drop an assume where
-  modifying the ``llvm.assume`` call cannot be replaced or dropped.
+  the ``llvm.assume`` call cannot be replaced or dropped.
 
 ``"nonnull"(ptr %p)``
   Equivalent to :ref:`nonnull <attr_nonnull>` on ``%p``.



More information about the llvm-commits mailing list