[llvm-branch-commits] [llvm] ec2ca01 - Revert "[IR] Reject unhandled assume bundles and seperate them from normal at…"
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Mon Jun 8 06:35:56 PDT 2026
Author: Nikolas Klauser
Date: 2026-06-08T15:35:51+02:00
New Revision: ec2ca011969b871f0a8bc6d55fb44de72d219dc0
URL: https://github.com/llvm/llvm-project/commit/ec2ca011969b871f0a8bc6d55fb44de72d219dc0
DIFF: https://github.com/llvm/llvm-project/commit/ec2ca011969b871f0a8bc6d55fb44de72d219dc0.diff
LOG: Revert "[IR] Reject unhandled assume bundles and seperate them from normal at…"
This reverts commit 377e0fb58d2bd7c933aea0c00076e75ba6d52896.
Added:
llvm/test/Analysis/ValueTracking/assume-queries-counter.ll
Modified:
llvm/docs/LangRef.rst
llvm/docs/ReleaseNotes.md
llvm/include/llvm/IR/InstrTypes.h
llvm/include/llvm/Transforms/Utils/PredicateInfo.h
llvm/lib/Analysis/LazyValueInfo.cpp
llvm/lib/Analysis/ValueTracking.cpp
llvm/lib/IR/CMakeLists.txt
llvm/lib/IR/Verifier.cpp
llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
llvm/lib/Transforms/Utils/AssumeBundleBuilder.cpp
llvm/lib/Transforms/Utils/PredicateInfo.cpp
llvm/test/Analysis/ValueTracking/assume.ll
llvm/test/Transforms/AlignmentFromAssumptions/simple.ll
llvm/test/Transforms/Attributor/nofpclass.ll
llvm/test/Transforms/Attributor/nofree.ll
llvm/test/Transforms/InstCombine/assume.ll
llvm/test/Transforms/Util/assume-builder.ll
llvm/test/Verifier/assume-bundles.ll
llvm/unittests/Analysis/AssumeBundleQueriesTest.cpp
Removed:
llvm/include/llvm/IR/BundleAttributes.def
llvm/include/llvm/IR/BundleAttributes.h
llvm/lib/IR/BundleAttributes.cpp
################################################################################
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index ca1f007865c03..5e5ebdf80773f 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1587,8 +1587,6 @@ 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
@@ -1600,8 +1598,6 @@ 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
@@ -1614,9 +1610,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``.
-
-.. _attr_dereferenceable_or_null:
+ implies ``noundef``. When used in an assume operand bundle, more restricted
+ semantics apply. See :ref:`assume operand bundles <assume_opbundles>` for
+ more details.
``dereferenceable_or_null(<n>)``
This indicates that the parameter or return value isn't both
@@ -1671,8 +1667,6 @@ 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
@@ -2155,9 +2149,6 @@ 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
@@ -3136,57 +3127,33 @@ Assume Operand Bundles
^^^^^^^^^^^^^^^^^^^^^^
Operand bundles on an :ref:`llvm.assume <int_assume>` allow representing
-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>`.
+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>`.
Assumes with operand bundles must have ``i1 true`` as the condition operand.
-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.
+An assume operand bundle has the form:
-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)]
-
-The following attributes are currently accepted:
-
-``"align"(ptr %p, i64 %align)``, ``"align"(ptr %p, i64 %align, i64 %offset)``
- Equivalent to :ref:`align(%align) <attr_align>` on ``%p``, or ``%p - %offset``
- if the ``%offset`` argument exists, except that ``%align`` may be a
- non-power-of-two alignment (including a zero alignment). If ``%align`` is not
- a power of two the pointer value must be all-zero. Otherwise the behavior is
- undefined.
-
-``"cold"()``
- Equivalent to :ref:`cold <attr_cold>`.
+::
-``"dereferenceable"(ptr %p, i64 %size)``
- Equivalent to :ref:`dereferenceable(%size) <attr_dereferenceable>` on ``%p``,
- except that ``%size`` may also be zero, in which case the bundle doesn't
- imply ``nonnull``.
+ "<tag>"([ <arguments>] ])
-``"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.
+In the case of function or parameter attributes, the operand bundle has the
+restricted form:
-``"ignore"(...)``
- Doesn't imply anything and is ignored. This is used to drop an assume where
- the ``llvm.assume`` call cannot be replaced or dropped.
+::
-``"nonnull"(ptr %p)``
- Equivalent to :ref:`nonnull <attr_nonnull>` on ``%p``.
+ "<tag>"([ <holds for value> [, <attribute argument>] ])
-``"noundef"(any_type %v)``
- Equivalent to :ref:`noundef <attr_noundef>` on ``%v``.
+* 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.
-``"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.
+If there are no arguments the attribute is a property of the call location.
For example:
@@ -3204,6 +3171,39 @@ allows the optimizer to assume that at location of call to
allows the optimizer to assume that the :ref:`llvm.assume <int_assume>`
call location is cold and that ``%val`` may not be null.
+Just like for the argument of :ref:`llvm.assume <int_assume>`, if any of the
+provided guarantees are violated at runtime the behavior is undefined.
+
+While attributes expect constant arguments, assume operand bundles may be
+provided a dynamic value, for example:
+
+.. code-block:: llvm
+
+ call void @llvm.assume(i1 true) ["align"(ptr %val, i32 %align)]
+
+If the operand bundle value violates any requirements on the attribute value,
+the behavior is undefined, unless one of the following exceptions applies:
+
+* ``"align"`` operand bundles may specify a non-power-of-two alignment
+ (including a zero alignment). If this is the case, then the pointer value
+ must be an all-zero pointer, otherwise the behavior is undefined.
+
+* ``dereferenceable(<n>)`` operand bundles only guarantee the pointer is
+ dereferenceable at the point of the assumption. The pointer may not be
+ dereferenceable at later pointers, e.g., because it could have been freed.
+ Only ``n > 0`` implies that the pointer is dereferenceable.
+
+In addition to allowing operand bundles encoding function and parameter
+attributes, an assume operand bundle may also encode a ``separate_storage``
+operand bundle. This has the form:
+
+.. code-block:: llvm
+
+ separate_storage(<val1>, <val2>)``
+
+This indicates that no pointer :ref:`based <pointeraliasing>` on one of its
+arguments can alias any pointer based on the other.
+
Even if the assumed property can be encoded as a boolean value, like
``nonnull``, using operand bundles to express the property can still have
benefits:
@@ -8143,7 +8143,7 @@ Together these two attributes provide a four-way classification:
- ``body`` only: main vectorized loop body
- ``epilogue`` only: scalar epilogue loop after vectorization
-- Both ``body`` and ``epilogue``: vectorized epilogue
+- Both ``body`` and ``epilogue``: vectorized epilogue
(a remainder loop that was itself vectorized during epilogue
vectorization)
- Neither: a plain loop not produced by the vectorizer
@@ -8772,7 +8772,7 @@ is executed, followed by ``uint64_t`` value and execution count pairs.
The value profiling kind is 0 for indirect call targets and 1 for memory
operations. For indirect call targets, each profile value is a hash
of the callee function name, and for memory operations each value is the
-byte length. It is illegal to have duplicate profile values (e.g.,
+byte length. It is illegal to have duplicate profile values (e.g.,
duplicate function hashes for indirect calls or byte lengths for memory
operations).
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index 3b862814bdbad..f234e9b5d6433 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -87,11 +87,6 @@ Makes programs 10x faster by doing Special New Thing.
and `ccc` agree for `void(ptr)` (x86_64, AArch64, RISC-V, ...) but is an ABI
break on i686, MIPS O32, PowerPC64 ELFv1, and Lanai.
-* Assume bundles now only accept attributes that are actually handled.
- Specifically, they are ``align``, ``cold``, ``dereferenceable``,
- ``dereferenceable_or_null``, ``nonnull``, ``noundef`` and
- ``separate_storage``.
-
* Fast math flags are now permitted on `uitofp` and `sitofp`.
### Changes to LLVM infrastructure
diff --git a/llvm/include/llvm/IR/BundleAttributes.def b/llvm/include/llvm/IR/BundleAttributes.def
deleted file mode 100644
index 78c692393fb44..0000000000000
--- a/llvm/include/llvm/IR/BundleAttributes.def
+++ /dev/null
@@ -1,22 +0,0 @@
-//===- llvm/BundleAttributes.def - LLVM Bundle Attributes -------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-ATTR(Align, align)
-ATTR(Cold, cold)
-ATTR(Dereferenceable, dereferenceable)
-ATTR(DereferenceableOrNull, dereferenceable_or_null)
-ATTR(Ignore, ignore)
-ATTR(NonNull, nonnull)
-
-// FIXME: Is this actually useful?
-// It's currently exclusively emitted through tests.
-ATTR(NoUndef, noundef)
-
-ATTR(SeparateStorage, separate_storage)
-
-#undef ATTR
diff --git a/llvm/include/llvm/IR/BundleAttributes.h b/llvm/include/llvm/IR/BundleAttributes.h
deleted file mode 100644
index ee6e5b4bc1ef6..0000000000000
--- a/llvm/include/llvm/IR/BundleAttributes.h
+++ /dev/null
@@ -1,63 +0,0 @@
-//===- llvm/BundleAttributes.h - LLVM Bundle Attributes ---------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_IR_BUNDLE_ATTRIBUTES_H
-#define LLVM_IR_BUNDLE_ATTRIBUTES_H
-
-#include "llvm/ADT/StringRef.h"
-#include "llvm/IR/InstrTypes.h"
-
-enum class BundleAttr {
- None,
-#define ATTR(Name, String) Name,
-#include "BundleAttributes.def"
-};
-
-namespace llvm {
-
-LLVM_ABI StringRef getNameFromBundleAttr(BundleAttr);
-LLVM_ABI BundleAttr getBundleAttrFromString(StringRef);
-
-inline BundleAttr getBundleAttrFromOBU(OperandBundleUse OBU) {
- return getBundleAttrFromString(OBU.getTagName());
-}
-
-struct AssumeAlignInfo {
- const Use &Ptr;
- const Use &Alignment;
- std::optional<uint64_t> AlignmentVal;
- std::optional<uint64_t> OffsetVal;
-};
-
-LLVM_ABI AssumeAlignInfo getAssumeAlignInfo(OperandBundleUse);
-
-struct AssumeDereferenceableInfo {
- const Use &Ptr;
- const Use &Count;
-};
-
-LLVM_ABI
-AssumeDereferenceableInfo getAssumeDereferenceableInfo(OperandBundleUse);
-
-struct AssumeNonNullInfo {
- const Use &Ptr;
-};
-
-LLVM_ABI AssumeNonNullInfo getAssumeNonNullInfo(OperandBundleUse);
-
-struct AssumeSeparateStorageInfo {
- const Use &Ptr1;
- const Use &Ptr2;
-};
-
-LLVM_ABI
-AssumeSeparateStorageInfo getAssumeSeparateStorageInfo(OperandBundleUse);
-
-} // namespace llvm
-
-#endif // LLVM_IR_BUNDLE_ATTRIBUTES_H
diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h
index 5f7df6a4eb6f8..b575b32a244df 100644
--- a/llvm/include/llvm/IR/InstrTypes.h
+++ b/llvm/include/llvm/IR/InstrTypes.h
@@ -2353,12 +2353,6 @@ 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 06ce720bda6a9..e472656d7b9fb 100644
--- a/llvm/include/llvm/Transforms/Utils/PredicateInfo.h
+++ b/llvm/include/llvm/Transforms/Utils/PredicateInfo.h
@@ -52,7 +52,6 @@
#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"
@@ -133,9 +132,9 @@ class PredicateAssume : public PredicateBase {
class PredicateBundleAssume : public PredicateAssume {
public:
- BundleAttr AttrKind;
+ Attribute::AttrKind AttrKind;
PredicateBundleAssume(Value *Op, IntrinsicInst *AssumeInst,
- BundleAttr AttrKind)
+ Attribute::AttrKind AttrKind)
: PredicateAssume(PT_BundleAssume, Op, AssumeInst, nullptr),
AttrKind(AttrKind) {}
diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp
index 3a875bd680fb9..e78a47b8de68d 100644
--- a/llvm/lib/Analysis/LazyValueInfo.cpp
+++ b/llvm/lib/Analysis/LazyValueInfo.cpp
@@ -14,6 +14,7 @@
#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"
@@ -22,7 +23,6 @@
#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,24 +856,26 @@ void LazyValueInfoImpl::intersectAssumeOrGuardBlockValueConstantRange(
continue;
if (AssumeVH.Index != AssumptionCache::ExprResultIdx) {
- auto OBU = I->getOperandBundleAt(AssumeVH.Index);
- switch (getBundleAttrFromOBU(OBU)) {
- case BundleAttr::NonNull:
- assert(getAssumeNonNullInfo(OBU).Ptr == Val);
- BBLV = BBLV.intersect(ValueLatticeElement::getNot(
- Constant::getNullValue(Val->getType())));
- break;
-
- case BundleAttr::Dereferenceable: {
- auto [Ptr, Count] = getAssumeDereferenceableInfo(OBU);
- assert(Ptr == Val);
- if (auto *CI = dyn_cast<ConstantInt>(Count); CI && !CI->isZero())
+ 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(Val->getType())));
- } break;
+ Constant::getNullValue(RK.WasOn->getType())));
+ break;
- default:
- 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;
+
+ 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 a646dcd41fa23..1261664c5b986 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -38,7 +38,6 @@
#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"
@@ -828,30 +827,32 @@ static bool isKnownNonZeroFromAssume(const Value *V, const SimplifyQuery &Q) {
"Got assumption for the wrong function!");
if (Elem.Index != AssumptionCache::ExprResultIdx) {
- bool AssumeImpliesNonNull = [&]() {
- auto OBU = I->getOperandBundleAt(Elem.Index);
- switch (getBundleAttrFromOBU(OBU)) {
- case BundleAttr::Dereferenceable: {
- auto [Ptr, Count] = getAssumeDereferenceableInfo(OBU);
- assert(Ptr == V);
- if (NullPointerIsDefined(Q.CxtI->getFunction(),
- V->getType()->getPointerAddressSpace()))
- return false;
+ 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 *CI = dyn_cast<ConstantInt>(Count);
- return CI && !CI->isZero();
- }
+ if (RK.AttrKind == Attribute::Dereferenceable) {
+ if (NullPointerIsDefined(Q.CxtI->getFunction(),
+ V->getType()->getPointerAddressSpace()))
+ return false;
+ assert(RK.IRArgValue &&
+ "Dereferenceable attribute without IR argument?");
- case BundleAttr::NonNull:
- assert(getAssumeNonNullInfo(OBU).Ptr == V);
- return true;
+ auto *CI = dyn_cast<ConstantInt>(RK.IRArgValue);
+ return CI && !CI->isZero();
+ }
- default:
return false;
- }
- }();
- if (AssumeImpliesNonNull && isValidAssumeForContext(I, Q))
- return true;
+ }();
+ if (AssumeImpliesNonNull && isValidAssumeForContext(I, Q))
+ return true;
+ }
continue;
}
@@ -1087,15 +1088,13 @@ void llvm::computeKnownBitsFromContext(const Value *V, KnownBits &Known,
"Got assumption for the wrong function!");
if (Elem.Index != AssumptionCache::ExprResultIdx) {
- if (auto OBU = I->getOperandBundleAt(Elem.Index);
- getBundleAttrFromOBU(OBU) == BundleAttr::Align) {
- auto [Ptr, _, Alignment, Offset] = getAssumeAlignInfo(OBU);
- assert(Ptr == V);
- if (!Alignment || !Offset || !isPowerOf2_64(*Alignment))
- continue;
- auto AlignVal = MinAlign(*Offset, *Alignment);
- if (isValidAssumeForContext(I, Q))
- Known.Zero.setLowBits(Log2_64(AlignVal));
+ 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));
}
continue;
}
diff --git a/llvm/lib/IR/BundleAttributes.cpp b/llvm/lib/IR/BundleAttributes.cpp
deleted file mode 100644
index 3e4e65e8fd3d1..0000000000000
--- a/llvm/lib/IR/BundleAttributes.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-//===- llvm/BundleAttributes.cpp - LLVM Bundle Attributes -------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "llvm/IR/BundleAttributes.h"
-
-#include "llvm/ADT/StringSwitch.h"
-#include "llvm/IR/Constants.h"
-
-using namespace llvm;
-
-StringRef llvm::getNameFromBundleAttr(BundleAttr BA) {
- switch (BA) {
-#define ATTR(Name, Str) \
- case BundleAttr::Name: \
- return #Str;
-#include "llvm/IR/BundleAttributes.def"
- case BundleAttr::None:
- return "none";
- }
-}
-
-BundleAttr llvm::getBundleAttrFromString(StringRef Str) {
- return StringSwitch<BundleAttr>(Str)
-#define ATTR(Name, Str) .Case(#Str, BundleAttr::Name)
-#include "llvm/IR/BundleAttributes.def"
- .Default(BundleAttr::None);
-}
-
-AssumeAlignInfo llvm::getAssumeAlignInfo(OperandBundleUse OBU) {
- assert(OBU.getTagName() == "align" && OBU.Inputs.size() >= 2 &&
- OBU.Inputs.size() <= 3);
- AssumeAlignInfo Ret{OBU.Inputs[0], OBU.Inputs[1], std::nullopt, std::nullopt};
- if (auto *Align = dyn_cast<ConstantInt>(OBU.Inputs[1]))
- Ret.AlignmentVal = Align->getZExtValue();
- if (OBU.Inputs.size() == 3) {
- if (auto *Offset = dyn_cast<ConstantInt>(OBU.Inputs[2]))
- Ret.OffsetVal = Offset->getZExtValue();
- } else {
- Ret.OffsetVal = 0;
- }
- return Ret;
-}
-
-AssumeSeparateStorageInfo
-llvm::getAssumeSeparateStorageInfo(OperandBundleUse OBU) {
- assert(OBU.getTagName() == "separate_storage" && OBU.Inputs.size() == 2);
- return {OBU.Inputs[0], OBU.Inputs[1]};
-}
-
-AssumeNonNullInfo llvm::getAssumeNonNullInfo(OperandBundleUse OBU) {
- assert(OBU.getTagName() == "nonnull" && OBU.Inputs.size() == 1);
- return {OBU.Inputs[0]};
-}
-
-AssumeDereferenceableInfo
-llvm::getAssumeDereferenceableInfo(OperandBundleUse OBU) {
- assert(OBU.getTagName() == "dereferenceable" && OBU.Inputs.size() == 2);
- return {OBU.Inputs[0], OBU.Inputs[1]};
-}
diff --git a/llvm/lib/IR/CMakeLists.txt b/llvm/lib/IR/CMakeLists.txt
index 9cc45ef0e1773..5511c3b33b0d7 100644
--- a/llvm/lib/IR/CMakeLists.txt
+++ b/llvm/lib/IR/CMakeLists.txt
@@ -6,7 +6,6 @@ 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 cb28ff5c633f3..344314e39c38a 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -64,7 +64,6 @@
#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"
@@ -6078,67 +6077,56 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
Check(Cond && Cond->isOne(),
"assume with operand bundles must have i1 true condition", Call);
}
- for (auto OBU : Call.operand_bundles()) {
+ for (auto &Elem : Call.bundle_op_infos()) {
+ unsigned ArgCount = Elem.End - Elem.Begin;
// Separate storage assumptions are special insofar as they're the only
// operand bundles allowed on assumes that aren't parameter attributes.
-
- 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(GetTypeAt(0)->isPointerTy(), "first argument should be a pointer",
- Call);
- Check(GetTypeAt(1)->isIntegerTy() &&
- GetTypeAt(1)->getIntegerBitWidth() <= 64,
- "second argument should be an integer with a maximum width of 64 "
- "bits",
- Call);
- Check(OBU.Inputs.size() < 3 ||
- GetTypeAt(2)->isIntegerTy() &&
- GetTypeAt(2)->getIntegerBitWidth() <= 64,
- "third argument should be an integer with a maximum width of 64 "
- "bits if present",
- Call);
- break;
- case BundleAttr::Cold:
- Check(OBU.Inputs.size() == 0,
- "cold assumptions should have no arguments", Call);
- break;
- case BundleAttr::Dereferenceable:
- case BundleAttr::DereferenceableOrNull:
- Check(OBU.Inputs.size() == 2,
- "dereferenceable assumptions should have 2 arguments", Call);
- Check(GetTypeAt(0)->isPointerTy(), "first argument should be a pointer",
- Call);
- Check(GetTypeAt(1)->isIntegerTy(),
- "second argument should be an integer", Call);
- break;
- 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,
+ if (Elem.Tag->getKey() == "separate_storage") {
+ Check(ArgCount == 2,
"separate_storage assumptions should have 2 arguments", Call);
- Check(GetTypeAt(0)->isPointerTy() && GetTypeAt(1)->isPointerTy(),
+ 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,
+ "alignment assumptions should have 2 or 3 arguments", Call);
+ Check(Call.getOperand(Elem.Begin)->getType()->isPointerTy(),
+ "first argument should be a pointer", Call);
+ Check(Call.getOperand(Elem.Begin + 1)->getType()->isIntegerTy(),
+ "second argument should be an integer", Call);
+ if (ArgCount == 3)
+ Check(Call.getOperand(Elem.Begin + 2)->getType()->isIntegerTy(),
+ "third argument should be an integer if present", Call);
+ continue;
+ }
+ if (Kind == Attribute::Dereferenceable) {
+ Check(ArgCount == 2,
+ "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(),
+ "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);
}
}
break;
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index c7c153df27f62..7bcaa930511ee 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -31,7 +31,6 @@
#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"
@@ -3648,45 +3647,66 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
return eraseInstFromFunction(*II);
}
- for (auto [Idx, OBU] : llvm::enumerate(II->operand_bundles())) {
- switch (getBundleAttrFromOBU(OBU)) {
- case BundleAttr::None:
- llvm_unreachable("Unexpected Attribute");
- case BundleAttr::Align: {
- // Try to remove redundant alignment assumptions.
- auto [Ptr, _, Alignment, Offset] = getAssumeAlignInfo(OBU);
+ for (unsigned Idx = 0; Idx < II->getNumOperandBundles(); Idx++) {
+ OperandBundleUse OBU = II->getOperandBundleAt(Idx);
- if (!Alignment || !Offset || *Offset != 0 || !isPowerOf2_64(*Alignment))
- break;
+ // 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;
// Remove align 1 bundles; they don't add any useful information.
- if (*Alignment == 1)
+ if (RK.ArgValue == 1)
return CallBase::removeOperandBundleAt(II, Idx);
// Don't try to remove align assumptions for pointers derived from
// arguments. We might lose information if the function gets inline and
// the align argument attribute disappears.
- Value *UO = getUnderlyingObject(Ptr);
+ Value *UO = getUnderlyingObject(RK.WasOn);
if (!UO || isa<Argument>(UO))
- break;
+ continue;
// Compute known bits for the pointer and drop the assume if the
// known alignment isn't increased by it.
- if (computeKnownBits(Ptr, II).countMinTrailingZeros() <
- Log2_64(*Alignment))
+ if (computeKnownBits(RK.WasOn, II).countMinTrailingZeros() <
+ Log2_64(RK.ArgValue))
continue;
return CallBase::removeOperandBundleAt(II, Idx);
}
- case BundleAttr::NonNull: {
- auto [Ptr] = llvm::getAssumeNonNullInfo(OBU);
+ 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;
// Drop assume if we can prove nonnull without it
- if (isKnownNonZero(Ptr, getSimplifyQuery().getWithInstruction(II)))
+ if (isKnownNonZero(RK.WasOn, getSimplifyQuery().getWithInstruction(II)))
return CallBase::removeOperandBundleAt(II, Idx);
// Fold the assume into metadata if it's valid at the load
- if (auto *LI = dyn_cast<LoadInst>(Ptr);
+ if (auto *LI = dyn_cast<LoadInst>(RK.WasOn);
LI &&
isValidAssumeForContext(II, LI, &DT, /*AllowEphemerals=*/true)) {
MDNode *MD = MDNode::get(II->getContext(), {});
@@ -3696,37 +3716,6 @@ 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 722d04c9ae957..26bca70e1056f 100644
--- a/llvm/lib/Transforms/Utils/AssumeBundleBuilder.cpp
+++ b/llvm/lib/Transforms/Utils/AssumeBundleBuilder.cpp
@@ -20,12 +20,18 @@
#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(
@@ -187,7 +193,8 @@ struct AssumeBuilderState {
void addAttribute(Attribute Attr, Value *WasOn) {
if (Attr.isTypeAttribute() || Attr.isStringAttribute() ||
- !isUsefullToPreserve(Attr.getKindAsEnum()))
+ (!ShouldPreserveAllAttributes &&
+ !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 7a86d310f0f93..3d3c70522fb55 100644
--- a/llvm/lib/Transforms/Utils/PredicateInfo.cpp
+++ b/llvm/lib/Transforms/Utils/PredicateInfo.cpp
@@ -17,7 +17,6 @@
#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"
@@ -364,12 +363,12 @@ void PredicateInfoBuilder::processAssume(
AssumeInst *II, BasicBlock *AssumeBB,
SmallVectorImpl<Value *> &OpsToRename) {
if (II->hasOperandBundles()) {
- 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));
+ 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));
}
}
return;
@@ -725,7 +724,7 @@ PredicateInfo::PredicateInfo(Function &F, DominatorTree &DT,
std::optional<PredicateConstraint> PredicateBase::getConstraint() const {
switch (Type) {
case PT_BundleAssume: {
- assert(cast<PredicateBundleAssume>(this)->AttrKind == BundleAttr::NonNull &&
+ assert(cast<PredicateBundleAssume>(this)->AttrKind == Attribute::NonNull &&
"Cannot handle anything other than NonNull");
return {{CmpInst::ICMP_NE, ConstantPointerNull::get(
cast<PointerType>(OriginalOp->getType()))}};
@@ -845,7 +844,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: " << getNameFromBundleAttr(PBA->AttrKind);
+ OS << " Attribute: " << Attribute::getNameFromAttrKind(PBA->AttrKind);
} else {
assert(isa<PredicateConditionAssume>(PA));
OS << " Comparison:" << *PA->Condition;
diff --git a/llvm/test/Analysis/ValueTracking/assume-queries-counter.ll b/llvm/test/Analysis/ValueTracking/assume-queries-counter.ll
new file mode 100644
index 0000000000000..0cb8279f3db38
--- /dev/null
+++ b/llvm/test/Analysis/ValueTracking/assume-queries-counter.ll
@@ -0,0 +1,112 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+
+; RUN: opt < %s -passes=instcombine --debug-counter=assume-queries-counter=1 -S | FileCheck %s --check-prefixes=COUNTER1
+; RUN: opt < %s -passes=instcombine --debug-counter=assume-queries-counter=2-3 -S | FileCheck %s --check-prefixes=COUNTER2
+; RUN: opt < %s -passes=instcombine --debug-counter=assume-queries-counter=4-7 -S | FileCheck %s --check-prefixes=COUNTER3
+
+declare i1 @get_val()
+declare void @llvm.assume(i1)
+
+define dso_local i1 @test1(ptr readonly %0) {
+; COUNTER1-LABEL: @test1(
+; COUNTER1-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[TMP0:%.*]]) ]
+; COUNTER1-NEXT: ret i1 false
+;
+; COUNTER2-LABEL: @test1(
+; COUNTER2-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[TMP0:%.*]]) ]
+; COUNTER2-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
+; COUNTER2-NEXT: ret i1 [[TMP2]]
+;
+; COUNTER3-LABEL: @test1(
+; COUNTER3-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[TMP0:%.*]]) ]
+; COUNTER3-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
+; COUNTER3-NEXT: ret i1 [[TMP2]]
+;
+ call void @llvm.assume(i1 true) ["nonnull"(ptr %0)]
+ %2 = icmp eq ptr %0, null
+ ret i1 %2
+}
+
+define dso_local i1 @test2(ptr readonly %0) {
+; COUNTER1-LABEL: @test2(
+; COUNTER1-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0:%.*]], null
+; COUNTER1-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[TMP0]]) ]
+; COUNTER1-NEXT: ret i1 [[TMP2]]
+;
+; COUNTER2-LABEL: @test2(
+; COUNTER2-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[TMP0:%.*]]) ]
+; COUNTER2-NEXT: ret i1 false
+;
+; COUNTER3-LABEL: @test2(
+; COUNTER3-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0:%.*]], null
+; COUNTER3-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[TMP0]]) ]
+; COUNTER3-NEXT: ret i1 [[TMP2]]
+;
+ %2 = icmp eq ptr %0, null
+ call void @llvm.assume(i1 true) ["nonnull"(ptr %0)]
+ ret i1 %2
+}
+
+define dso_local i32 @test4(ptr readonly %0, i1 %cond) nofree nosync {
+; COUNTER1-LABEL: @test4(
+; COUNTER1-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0:%.*]], i32 4) ]
+; COUNTER1-NEXT: br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]]
+; COUNTER1: B:
+; COUNTER1-NEXT: br label [[A]]
+; COUNTER1: A:
+; COUNTER1-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
+; COUNTER1-NEXT: br i1 [[TMP2]], label [[TMP5:%.*]], label [[TMP3:%.*]]
+; COUNTER1: 3:
+; COUNTER1-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP0]], align 4
+; COUNTER1-NEXT: br label [[TMP5]]
+; COUNTER1: 5:
+; COUNTER1-NEXT: [[TMP6:%.*]] = phi i32 [ [[TMP4]], [[TMP3]] ], [ 0, [[A]] ]
+; COUNTER1-NEXT: ret i32 [[TMP6]]
+;
+; COUNTER2-LABEL: @test4(
+; COUNTER2-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0:%.*]], i32 4) ]
+; COUNTER2-NEXT: br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]]
+; COUNTER2: B:
+; COUNTER2-NEXT: br label [[A]]
+; COUNTER2: A:
+; COUNTER2-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
+; COUNTER2-NEXT: br i1 [[TMP2]], label [[TMP5:%.*]], label [[TMP3:%.*]]
+; COUNTER2: 3:
+; COUNTER2-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP0]], align 4
+; COUNTER2-NEXT: br label [[TMP5]]
+; COUNTER2: 5:
+; COUNTER2-NEXT: [[TMP6:%.*]] = phi i32 [ [[TMP4]], [[TMP3]] ], [ 0, [[A]] ]
+; COUNTER2-NEXT: ret i32 [[TMP6]]
+;
+; COUNTER3-LABEL: @test4(
+; COUNTER3-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0:%.*]], i32 4) ]
+; COUNTER3-NEXT: br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]]
+; COUNTER3: B:
+; COUNTER3-NEXT: br label [[A]]
+; COUNTER3: A:
+; COUNTER3-NEXT: br i1 false, label [[TMP4:%.*]], label [[TMP2:%.*]]
+; COUNTER3: 2:
+; COUNTER3-NEXT: [[TMP3:%.*]] = load i32, ptr [[TMP0]], align 4
+; COUNTER3-NEXT: br label [[TMP4]]
+; COUNTER3: 4:
+; COUNTER3-NEXT: [[TMP5:%.*]] = phi i32 [ [[TMP3]], [[TMP2]] ], [ poison, [[A]] ]
+; COUNTER3-NEXT: ret i32 [[TMP5]]
+;
+ call void @llvm.assume(i1 true) ["dereferenceable"(ptr %0, i32 4)]
+ br i1 %cond, label %A, label %B
+
+B:
+ br label %A
+
+A:
+ %2 = icmp eq ptr %0, null
+ br i1 %2, label %5, label %3
+
+3: ; preds = %1
+ %4 = load i32, ptr %0, align 4
+ br label %5
+
+5: ; preds = %1, %3
+ %6 = phi i32 [ %4, %3 ], [ 0, %A ]
+ ret i32 %6
+}
diff --git a/llvm/test/Analysis/ValueTracking/assume.ll b/llvm/test/Analysis/ValueTracking/assume.ll
index c5fe253634716..dafc15c2b2d23 100644
--- a/llvm/test/Analysis/ValueTracking/assume.ll
+++ b/llvm/test/Analysis/ValueTracking/assume.ll
@@ -191,72 +191,3 @@ define i1 @test_dereferenceable_non_zero_size_is_nonnull(ptr %ptr) {
%2 = icmp eq ptr %ptr, null
ret i1 %2
}
-
-define i1 @test_align_with_constant_offset_0(ptr %ptr) {
-; CHECK-LABEL: @test_align_with_constant_offset_0(
-; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 8, i64 0) ]
-; CHECK-NEXT: ret i1 true
-;
- call void @llvm.assume(i1 true) ["align"(ptr %ptr, i64 8, i64 0)]
- %intptr = ptrtoint ptr %ptr to i64
- %and = and i64 %intptr, 7
- %is_aligned = icmp eq i64 %and, 0
- ret i1 %is_aligned
-}
-
-define i1 @test_align_with_constant_offset_1(ptr %ptr) {
-; CHECK-LABEL: @test_align_with_constant_offset_1(
-; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 8, i64 1) ]
-; CHECK-NEXT: [[INTPTR:%.*]] = ptrtoint ptr [[PTR]] to i64
-; CHECK-NEXT: [[AND:%.*]] = and i64 [[INTPTR]], 7
-; CHECK-NEXT: [[IS_ALIGNED:%.*]] = icmp eq i64 [[AND]], 0
-; CHECK-NEXT: ret i1 [[IS_ALIGNED]]
-;
- call void @llvm.assume(i1 true) ["align"(ptr %ptr, i64 8, i64 1)]
- %intptr = ptrtoint ptr %ptr to i64
- %and = and i64 %intptr, 7
- %is_aligned = icmp eq i64 %and, 0
- ret i1 %is_aligned
-}
-
-define i1 @test_align_with_constant_offset_4(ptr %ptr) {
-; CHECK-LABEL: @test_align_with_constant_offset_4(
-; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 8, i64 4) ]
-; CHECK-NEXT: [[INTPTR:%.*]] = ptrtoint ptr [[PTR]] to i64
-; CHECK-NEXT: [[AND:%.*]] = and i64 [[INTPTR]], 4
-; CHECK-NEXT: [[IS_ALIGNED:%.*]] = icmp eq i64 [[AND]], 0
-; CHECK-NEXT: ret i1 [[IS_ALIGNED]]
-;
- call void @llvm.assume(i1 true) ["align"(ptr %ptr, i64 8, i64 4)]
- %intptr = ptrtoint ptr %ptr to i64
- %and = and i64 %intptr, 7
- %is_aligned = icmp eq i64 %and, 0
- ret i1 %is_aligned
-}
-
-define i1 @test_align_with_constant_offset_8(ptr %ptr) {
-; CHECK-LABEL: @test_align_with_constant_offset_8(
-; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 8, i64 8) ]
-; CHECK-NEXT: ret i1 true
-;
- call void @llvm.assume(i1 true) ["align"(ptr %ptr, i64 8, i64 8)]
- %intptr = ptrtoint ptr %ptr to i64
- %and = and i64 %intptr, 7
- %is_aligned = icmp eq i64 %and, 0
- ret i1 %is_aligned
-}
-
-define i1 @test_align_with_variable_offset(ptr %ptr, i64 %offset) {
-; CHECK-LABEL: @test_align_with_variable_offset(
-; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 8, i64 [[OFFSET:%.*]]) ]
-; CHECK-NEXT: [[INTPTR:%.*]] = ptrtoint ptr [[PTR]] to i64
-; CHECK-NEXT: [[AND:%.*]] = and i64 [[INTPTR]], 7
-; CHECK-NEXT: [[IS_ALIGNED:%.*]] = icmp eq i64 [[AND]], 0
-; CHECK-NEXT: ret i1 [[IS_ALIGNED]]
-;
- call void @llvm.assume(i1 true) ["align"(ptr %ptr, i64 8, i64 %offset)]
- %intptr = ptrtoint ptr %ptr to i64
- %and = and i64 %intptr, 7
- %is_aligned = icmp eq i64 %and, 0
- ret i1 %is_aligned
-}
diff --git a/llvm/test/Transforms/AlignmentFromAssumptions/simple.ll b/llvm/test/Transforms/AlignmentFromAssumptions/simple.ll
index d908c2a5206fa..c2f2d21fafc40 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]], i64 32, i64 0) ]
+; CHECK-NEXT: tail call void @llvm.assume(i1 true) [ "align"(ptr [[A]], i128 32, i128 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, i64 32, i64 0)]
+ tail call void @llvm.assume(i1 true) ["align"(ptr %a, i128 32, i128 0)]
br label %for.body
for.body: ; preds = %entry, %for.body
@@ -399,3 +399,4 @@ declare void @llvm.assume(i1) nounwind
declare void @llvm.memset.p0.i64(ptr nocapture, i8, i64, i1) nounwind
declare void @llvm.memcpy.p0.p0.i64(ptr nocapture, ptr nocapture, i64, i1) nounwind
+
diff --git a/llvm/test/Transforms/Attributor/nofpclass.ll b/llvm/test/Transforms/Attributor/nofpclass.ll
index 8652049226c75..ac0aedc7716d5 100644
--- a/llvm/test/Transforms/Attributor/nofpclass.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass.ll
@@ -644,6 +644,34 @@ 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 6937238dc8c3c..94aa79aa327f4 100644
--- a/llvm/test/Transforms/Attributor/nofree.ll
+++ b/llvm/test/Transforms/Attributor/nofree.ll
@@ -343,6 +343,85 @@ 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)
@@ -419,6 +498,7 @@ 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 }
@@ -435,4 +515,5 @@ attributes #2 = { nobuiltin nounwind }
; CGSCC: attributes #[[ATTR12:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }
; CGSCC: attributes #[[ATTR13:[0-9]+]] = { nounwind willreturn }
; CGSCC: attributes #[[ATTR14]] = { nofree nosync willreturn }
+; CGSCC: attributes #[[ATTR15]] = { nofree willreturn memory(write) }
;.
diff --git a/llvm/test/Transforms/InstCombine/assume.ll b/llvm/test/Transforms/InstCombine/assume.ll
index c1ccb49209227..a154e254c138e 100644
--- a/llvm/test/Transforms/InstCombine/assume.ll
+++ b/llvm/test/Transforms/InstCombine/assume.ll
@@ -121,77 +121,6 @@ entry:
ret void
}
-define void @align_with_constant_offset_0(ptr %ptr) {
-; DEFAULT-LABEL: @align_with_constant_offset_0(
-; DEFAULT-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 16) ]
-; DEFAULT-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR]], i64 8, i64 0) ]
-; DEFAULT-NEXT: ret void
-;
-; BUNDLES-LABEL: @align_with_constant_offset_0(
-; BUNDLES-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 16, i64 0) ]
-; BUNDLES-NEXT: ret void
-;
- call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i64 16) ]
- call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i64 8, i64 0) ]
- ret void
-}
-
-define void @align_with_constant_offset_1(ptr %ptr) {
-; CHECK-LABEL: @align_with_constant_offset_1(
-; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 16) ]
-; CHECK-NEXT: [[PTR2:%.*]] = getelementptr i8, ptr [[PTR]], i64 9
-; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR2]], i64 8, i64 1) ]
-; CHECK-NEXT: ret void
-;
- call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i64 16) ]
- %ptr2 = getelementptr i8, ptr %ptr, i64 9
- call void @llvm.assume(i1 true) [ "align"(ptr %ptr2, i64 8, i64 1) ]
- ret void
-}
-
-define void @align_with_constant_offset_4(ptr %ptr) {
-; CHECK-LABEL: @align_with_constant_offset_4(
-; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 16) ]
-; CHECK-NEXT: [[PTR2:%.*]] = getelementptr i8, ptr [[PTR]], i64 4
-; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR2]], i64 8, i64 4) ]
-; CHECK-NEXT: ret void
-;
- call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i64 16) ]
- %ptr2 = getelementptr i8, ptr %ptr, i64 4
- call void @llvm.assume(i1 true) [ "align"(ptr %ptr2, i64 8, i64 4) ]
- ret void
-}
-
-define void @align_with_constant_offset_8(ptr %ptr) {
-; DEFAULT-LABEL: @align_with_constant_offset_8(
-; DEFAULT-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 16) ]
-; DEFAULT-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR]], i64 8, i64 8) ]
-; DEFAULT-NEXT: ret void
-;
-; BUNDLES-LABEL: @align_with_constant_offset_8(
-; BUNDLES-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 16, i64 8) ]
-; BUNDLES-NEXT: ret void
-;
- call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i64 16) ]
- call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i64 8, i64 8) ]
- ret void
-}
-
-define void @align_with_variable_offset(ptr %ptr, i64 %offset) {
-; DEFAULT-LABEL: @align_with_variable_offset(
-; DEFAULT-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 16) ]
-; DEFAULT-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR]], i64 8, i64 [[OFFSET:%.*]]) ]
-; DEFAULT-NEXT: ret void
-;
-; BUNDLES-LABEL: @align_with_variable_offset(
-; BUNDLES-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i64 16, i64 [[OFFSET:%.*]]) ]
-; BUNDLES-NEXT: ret void
-;
- call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i64 16) ]
- call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i64 8, i64 %offset) ]
- ret void
-}
-
define void @redundant_align() {
; CHECK-LABEL: @redundant_align(
; CHECK-NEXT: [[PTR:%.*]] = call ptr @get_ptr()
diff --git a/llvm/test/Transforms/Util/assume-builder.ll b/llvm/test/Transforms/Util/assume-builder.ll
index d5fc4ef57015d..98ff2a4827d61 100644
--- a/llvm/test/Transforms/Util/assume-builder.ll
+++ b/llvm/test/Transforms/Util/assume-builder.ll
@@ -1,5 +1,6 @@
; 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
@@ -46,6 +47,34 @@ 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:
@@ -172,6 +201,38 @@ 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:
@@ -330,6 +391,39 @@ 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:
@@ -483,6 +577,30 @@ 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:
@@ -596,6 +714,27 @@ 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:
@@ -694,6 +833,19 @@ 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:
@@ -766,6 +918,22 @@ 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:
@@ -857,6 +1025,36 @@ 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:
@@ -995,6 +1193,25 @@ 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 efba13dd7c7c5..728b118c99fb6 100644
--- a/llvm/test/Verifier/assume-bundles.ll
+++ b/llvm/test/Verifier/assume-bundles.ll
@@ -7,89 +7,32 @@ define void @func(ptr %P, i32 %P1, ptr %P2, ptr %P3, i1 %cond) {
; CHECK: tags must be valid attribute names
; CHECK: "adazdazd"
call void @llvm.assume(i1 true) ["adazdazd"()]
-; CHECK: assume with operand bundles must have i1 true condition
- call void @llvm.assume(i1 %cond) ["nonnull"(ptr %P)]
-
-; Align checks
-;
-; CHECK: alignment assumptions should have 2 or 3 arguments
- call void @llvm.assume(i1 true) ["align"(ptr %P)]
-; This one is valid
- call void @llvm.assume(i1 true) ["align"(ptr %P, i32 %P1)]
-; This one is valid
- call void @llvm.assume(i1 true) ["align"(ptr %P, i32 %P1, i32 4)]
-; CHECK: alignment assumptions should have 2 or 3 arguments
- call void @llvm.assume(i1 true) ["align"(ptr %P, i32 %P1, i32 4, i32 4)]
-; CHECK: first argument should be a pointer
- call void @llvm.assume(i1 true) ["align"(i32 %P1, ptr %P2)]
-; CHECK: second argument should be an integer with a maximum width of 64 bits
- call void @llvm.assume(i1 true) ["align"(ptr %P, ptr %P2)]
-; CHECK: second argument should be an integer with a maximum width of 64 bits
- call void @llvm.assume(i1 true) ["align"(ptr %P, i65 1)]
-; CHECK: third argument should be an integer with a maximum width of 64 bits if present
- call void @llvm.assume(i1 true) ["align"(ptr %P, i32 %P1, ptr %P2)]
-; CHECK: third argument should be an integer with a maximum width of 64 bits if present
- call void @llvm.assume(i1 true) ["align"(ptr %P, i32 %P1, i65 1)]
-
-; Cold checks
-;
-; CHECK: cold assumptions should have no arguments
- call void @llvm.assume(i1 true) ["cold"(ptr %P)]
-
-; Dereferenceable checks
-;
; CHECK-NOT: call{{.+}}deref
call void @llvm.assume(i1 true) ["dereferenceable"(ptr %P, i32 %P1)]
-; CHECK: first argument should be a pointer
- call void @llvm.assume(i1 true) ["dereferenceable"(i32 %P1, i32 %P1)]
; CHECK: second argument should be an integer
call void @llvm.assume(i1 true) ["dereferenceable"(ptr %P, float 1.5)]
; CHECK: dereferenceable assumptions should have 2 arguments
call void @llvm.assume(i1 true) ["dereferenceable"(ptr %P, i32 8, i32 8)]
; CHECK: dereferenceable assumptions should have 2 arguments
call void @llvm.assume(i1 true) ["dereferenceable"(ptr %P)]
-
-; 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: 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)]
; CHECK: second argument should be an integer
- 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
-;
+ 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)]
; 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 e99e2ec4930fc..485b70c068401 100644
--- a/llvm/unittests/Analysis/AssumeBundleQueriesTest.cpp
+++ b/llvm/unittests/Analysis/AssumeBundleQueriesTest.cpp
@@ -22,6 +22,10 @@
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 *)>>>
@@ -122,6 +126,16 @@ 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);
@@ -295,6 +309,19 @@ TEST(AssumeQueryAPI, fillMapFromAssume) {
ASSERT_TRUE(MapHasRightValue(
Map, Assume, {I->getOperand(0), Attribute::Alignment}, {64, 64}));
}));
+ Tests.push_back(std::make_pair(
+ "call void @func_many(ptr align 8 %P1) cold\n", [](Instruction *I) {
+ ShouldPreserveAllAttributes.setValue(true);
+ auto *Assume = buildAssumeFromInst(I);
+ Assume->insertBefore(I->getIterator());
+
+ RetainedKnowledgeMap Map;
+ fillMapFromAssume(*Assume, Map);
+
+ ASSERT_TRUE(FindExactlyAttributes(
+ Map, nullptr, "(nounwind|norecurse|willreturn|cold)"));
+ ShouldPreserveAllAttributes.setValue(false);
+ }));
Tests.push_back(
std::make_pair("call void @llvm.assume(i1 true)\n", [](Instruction *I) {
RetainedKnowledgeMap Map;
More information about the llvm-branch-commits
mailing list