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; _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
