[llvm] [Verifier] Accept range assume bundles (PR #202586)
Nikolas Klauser via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 22 04:57:13 PDT 2026
https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/202586
>From 78e0cd1be9f43a664d99f91f88991655999ef513 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 9 Jun 2026 13:32:27 +0200
Subject: [PATCH 1/5] [Verifier] Accept range assume bundles
This patch update the verifier to accept range bundles, but doesn't
actually do anything with them. Follow-up patches will add handling of
the bundle to the various passes that can make use of the information.
---
llvm/docs/LangRef.rst | 6 +++++
llvm/include/llvm/IR/BundleAttributes.def | 1 +
llvm/lib/IR/Verifier.cpp | 15 ++++++++++++
.../InstCombine/InstCombineCalls.cpp | 1 +
llvm/test/Verifier/assume-bundles.ll | 23 +++++++++++++++++++
5 files changed, 46 insertions(+)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index cf052513c5ef8..cae15365fe605 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1906,6 +1906,8 @@ Currently, only the following parameter attributes are defined:
This attribute cannot be applied to return values.
+.. _attr_range:
+
``range(<ty> <a>, <b>)``
This attribute expresses the possible range of the parameter or return value.
If the value is not in the specified range, it is converted to poison.
@@ -3184,6 +3186,10 @@ The following attributes are currently accepted:
``"noundef"(any_type %v)``
Equivalent to :ref:`noundef <attr_noundef>` on ``%v``.
+``"range"(iN %val, iN <lower_bound>, iN <upper_bound>)``
+ Equivalent to :ref:`range(iN <lower_bound>, <upper_bound>) <attr_range>` on
+ ``%val``.
+
``"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.
diff --git a/llvm/include/llvm/IR/BundleAttributes.def b/llvm/include/llvm/IR/BundleAttributes.def
index 78c692393fb44..cd1ec8d7ea20d 100644
--- a/llvm/include/llvm/IR/BundleAttributes.def
+++ b/llvm/include/llvm/IR/BundleAttributes.def
@@ -17,6 +17,7 @@ ATTR(NonNull, nonnull)
// It's currently exclusively emitted through tests.
ATTR(NoUndef, noundef)
+ATTR(Range, range)
ATTR(SeparateStorage, separate_storage)
#undef ATTR
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index f0363b6553440..848b98cfb4930 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -6135,6 +6135,21 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
Check(OBU.Inputs.size() == 1,
"noundef assumptions should have 1 argument", Call);
break;
+ case BundleAttr::Range:
+ Check(OBU.Inputs.size() == 3,
+ "range assumptions should have 3 arguments", Call);
+ Check(GetTypeAt(0)->isIntegerTy(),
+ "first argument should be an integer", Call);
+ Check(isa<ConstantInt>(OBU.Inputs[1]),
+ "second argument should be a constant integer", Call);
+ Check(isa<ConstantInt>(OBU.Inputs[2]),
+ "third argument should be a constant integer", Call);
+ Check(GetTypeAt(0)->getIntegerBitWidth() ==
+ GetTypeAt(1)->getIntegerBitWidth() &&
+ GetTypeAt(1)->getIntegerBitWidth() ==
+ GetTypeAt(2)->getIntegerBitWidth(),
+ "all integers should have the same bit width", Call);
+ break;
case BundleAttr::SeparateStorage:
Check(OBU.Inputs.size() == 2,
"separate_storage assumptions should have 2 arguments", Call);
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 64c311ffd6b33..279aff19e8ea7 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -3730,6 +3730,7 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
case BundleAttr::DereferenceableOrNull:
case BundleAttr::Ignore:
case BundleAttr::NoUndef:
+ case BundleAttr::Range:
break;
// This cannot be simplified
diff --git a/llvm/test/Verifier/assume-bundles.ll b/llvm/test/Verifier/assume-bundles.ll
index dd8d31031c4f9..9b37300860c56 100644
--- a/llvm/test/Verifier/assume-bundles.ll
+++ b/llvm/test/Verifier/assume-bundles.ll
@@ -80,6 +80,29 @@ define void @func(ptr %P, i32 %P1, ptr %P2, ptr %P3, i1 %cond) {
; CHECK: noundef assumptions should have 1 argument
call void @llvm.assume(i1 true) ["noundef"(ptr %P, ptr %P)]
+; Range checks
+;
+; CHECK: range assumptions should have 3 arguments
+ call void @llvm.assume(i1 true) ["range"()]
+; CHECK: range assumptions should have 3 arguments
+ call void @llvm.assume(i1 true) ["range"(i32 %P1)]
+; CHECK: range assumptions should have 3 arguments
+ call void @llvm.assume(i1 true) ["range"(i32 %P1, i32 0)]
+; This one is valid
+ call void @llvm.assume(i1 true) ["range"(i32 %P1, i32 0, i32 0)]
+; CHECK: range assumptions should have 3 arguments
+ call void @llvm.assume(i1 true) ["range"(i32 %P1, i32 0, i32 0, i32 0)]
+; CHECK: first argument should be an integer
+ call void @llvm.assume(i1 true) ["range"(ptr %P, i32 0, i32 0)]
+; CHECK: second argument should be a constant integer
+ call void @llvm.assume(i1 true) ["range"(i32 %P1, i32 %P1, i32 0)]
+; CHECK: third argument should be a constant integer
+ call void @llvm.assume(i1 true) ["range"(i32 %P1, i32 0, i32 %P1)]
+; CHECK: all integers should have the same bit width
+ call void @llvm.assume(i1 true) ["range"(i32 %P1, i32 0, i33 0)]
+; CHECK: all integers should have the same bit width
+ call void @llvm.assume(i1 true) ["range"(i32 %P1, i33 0, i32 0)]
+
; SeparateStorage checks
;
; CHECK: separate_storage assumptions should have 2 arguments
>From 60ec40d6ebf70030c08c6806e2d50ca0c47863fd Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 15 Jun 2026 16:46:10 +0200
Subject: [PATCH 2/5] Address comments
---
llvm/docs/LangRef.rst | 10 ++++++---
llvm/lib/IR/Verifier.cpp | 17 +++++++++------
llvm/test/Verifier/assume-bundles.ll | 32 +++++++++++++++-------------
3 files changed, 34 insertions(+), 25 deletions(-)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index cae15365fe605..e0f2d6e8ef8cf 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -3186,9 +3186,13 @@ The following attributes are currently accepted:
``"noundef"(any_type %v)``
Equivalent to :ref:`noundef <attr_noundef>` on ``%v``.
-``"range"(iN %val, iN <lower_bound>, iN <upper_bound>)``
- Equivalent to :ref:`range(iN <lower_bound>, <upper_bound>) <attr_range>` on
- ``%val``.
+``"range"(iN %val, iN %lower_bound, iN %upper_bound, i1 %wraps)``
+ Equivalent to :ref:`range(iN %lower_bound, %upper_bound) <attr_range>` on
+ ``%val``, except that ``%wraps`` determines whether the range is allowed to
+ wrap and ``%upper_bound`` is always allowed to be equal to ``%lower_bound``.
+ As a result, ``%upper_bound <= %lower_bound`` and ``%wraps`` being false
+ implies an empty range, while ``%lower_bound == %upper_bound`` and ``%wraps``
+ being true implies a full range (i.e. nothing).
``"separate_storage"(ptr %p1, ptr %p2)``
This indicates that no pointer :ref:`based <pointeraliasing>` on one of its
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 848b98cfb4930..8539c0f1c08b9 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -6136,19 +6136,22 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
"noundef assumptions should have 1 argument", Call);
break;
case BundleAttr::Range:
- Check(OBU.Inputs.size() == 3,
- "range assumptions should have 3 arguments", Call);
+ Check(OBU.Inputs.size() == 4,
+ "range assumptions should have 4 arguments", Call);
Check(GetTypeAt(0)->isIntegerTy(),
"first argument should be an integer", Call);
- Check(isa<ConstantInt>(OBU.Inputs[1]),
- "second argument should be a constant integer", Call);
- Check(isa<ConstantInt>(OBU.Inputs[2]),
- "third argument should be a constant integer", Call);
+ Check(GetTypeAt(1)->isIntegerTy(),
+ "second argument should be an integer", Call);
+ Check(GetTypeAt(2)->isIntegerTy(),
+ "third argument should be an integer", Call);
+ Check(isa<ConstantInt>(OBU.Inputs[3]) &&
+ cast<ConstantInt>(OBU.Inputs[3])->getBitWidth() == 1,
+ "fourth argument should be a constant bool", Call);
Check(GetTypeAt(0)->getIntegerBitWidth() ==
GetTypeAt(1)->getIntegerBitWidth() &&
GetTypeAt(1)->getIntegerBitWidth() ==
GetTypeAt(2)->getIntegerBitWidth(),
- "all integers should have the same bit width", Call);
+ "first three integers should have the same bit width", Call);
break;
case BundleAttr::SeparateStorage:
Check(OBU.Inputs.size() == 2,
diff --git a/llvm/test/Verifier/assume-bundles.ll b/llvm/test/Verifier/assume-bundles.ll
index 9b37300860c56..03e13e1fd7042 100644
--- a/llvm/test/Verifier/assume-bundles.ll
+++ b/llvm/test/Verifier/assume-bundles.ll
@@ -82,26 +82,28 @@ define void @func(ptr %P, i32 %P1, ptr %P2, ptr %P3, i1 %cond) {
; Range checks
;
-; CHECK: range assumptions should have 3 arguments
+; CHECK: range assumptions should have 4 arguments
call void @llvm.assume(i1 true) ["range"()]
-; CHECK: range assumptions should have 3 arguments
+; CHECK: range assumptions should have 4 arguments
call void @llvm.assume(i1 true) ["range"(i32 %P1)]
-; CHECK: range assumptions should have 3 arguments
+; CHECK: range assumptions should have 4 arguments
call void @llvm.assume(i1 true) ["range"(i32 %P1, i32 0)]
-; This one is valid
+; CHECK: range assumptions should have 4 arguments
call void @llvm.assume(i1 true) ["range"(i32 %P1, i32 0, i32 0)]
-; CHECK: range assumptions should have 3 arguments
- call void @llvm.assume(i1 true) ["range"(i32 %P1, i32 0, i32 0, i32 0)]
+; This one is valid
+ call void @llvm.assume(i1 true) ["range"(i32 %P1, i32 %P1, i32 0, i1 true)]
+; CHECK: range assumptions should have 4 arguments
+ call void @llvm.assume(i1 true) ["range"(i32 %P1, i32 0, i32 0, i32 0, i32 0)]
; CHECK: first argument should be an integer
- call void @llvm.assume(i1 true) ["range"(ptr %P, i32 0, i32 0)]
-; CHECK: second argument should be a constant integer
- call void @llvm.assume(i1 true) ["range"(i32 %P1, i32 %P1, i32 0)]
-; CHECK: third argument should be a constant integer
- call void @llvm.assume(i1 true) ["range"(i32 %P1, i32 0, i32 %P1)]
-; CHECK: all integers should have the same bit width
- call void @llvm.assume(i1 true) ["range"(i32 %P1, i32 0, i33 0)]
-; CHECK: all integers should have the same bit width
- call void @llvm.assume(i1 true) ["range"(i32 %P1, i33 0, i32 0)]
+ call void @llvm.assume(i1 true) ["range"(ptr %P, i32 0, i32 0, i1 true)]
+; CHECK: first three integers should have the same bit width
+ call void @llvm.assume(i1 true) ["range"(i32 %P1, i32 0, i33 0, i1 true)]
+; CHECK: first three integers should have the same bit width
+ call void @llvm.assume(i1 true) ["range"(i32 %P1, i33 0, i32 0, i1 true)]
+; CHECK: fourth argument should be a constant bool
+ call void @llvm.assume(i1 true) ["range"(i32 %P1, i32 0, i32 0, i2 0)]
+; CHECK: fourth argument should be a constant bool
+ call void @llvm.assume(i1 true) ["range"(i32 %P1, i32 0, i32 0, i1 %cond)]
; SeparateStorage checks
;
>From aacbe13eb34dd3a2bbb63202b0ccaf97d04fafaa Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 15 Jun 2026 16:47:17 +0200
Subject: [PATCH 3/5] Stuff
---
llvm/docs/LangRef.rst | 11 ++---------
1 file changed, 2 insertions(+), 9 deletions(-)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 7e6a7dab261d4..d345ca392544e 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -2863,8 +2863,7 @@ For example:
The following aspects are currently supported:
- - ``fixed``: The call has a C ISO 18037 fixed-point argument.
- - ``float``: The call has a floating-point argument.
+ - ``float``: The call has a floating point argument
@@ -3193,7 +3192,7 @@ The following attributes are currently accepted:
wrap and ``%upper_bound`` is always allowed to be equal to ``%lower_bound``.
As a result, ``%upper_bound <= %lower_bound`` and ``%wraps`` being false
implies an empty range, while ``%lower_bound == %upper_bound`` and ``%wraps``
- being true implies a full range (i.e. nothing).
+ being true implies a full range.
``"separate_storage"(ptr %p1, ptr %p2)``
This indicates that no pointer :ref:`based <pointeraliasing>` on one of its
@@ -13166,9 +13165,6 @@ the default rounding mode.
If the ``nneg`` flag is set, and the ``uitofp`` argument is negative,
the result is a poison value.
-If the '``nsz``' flag is set and the input value is 0, the sign bit of
-the result is non-deterministic.
-
Example:
""""""""
@@ -13216,9 +13212,6 @@ quantity and converts it to the corresponding floating-point value. If the
value cannot be exactly represented, it is rounded using the default rounding
mode.
-If the '``nsz``' flag is set and the input value is 0, the sign bit of
-the result is non-deterministic.
-
Example:
""""""""
>From e0a3140cedae8b864e0b78f62d2baa16235a7bd2 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 16 Jun 2026 00:12:45 +0200
Subject: [PATCH 4/5] Switch to inclusive
---
llvm/docs/LangRef.rst | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index d345ca392544e..d9b7ceec96144 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -3186,13 +3186,14 @@ The following attributes are currently accepted:
``"noundef"(any_type %v)``
Equivalent to :ref:`noundef <attr_noundef>` on ``%v``.
-``"range"(iN %val, iN %lower_bound, iN %upper_bound, i1 %wraps)``
+``"range"(iN %val, iN %lower_bound, iN %upper_bound, i1 %inclusive)``
Equivalent to :ref:`range(iN %lower_bound, %upper_bound) <attr_range>` on
- ``%val``, except that ``%wraps`` determines whether the range is allowed to
- wrap and ``%upper_bound`` is always allowed to be equal to ``%lower_bound``.
- As a result, ``%upper_bound <= %lower_bound`` and ``%wraps`` being false
- implies an empty range, while ``%lower_bound == %upper_bound`` and ``%wraps``
- being true implies a full range.
+ ``%val``, except that ``%inclusive`` determines whether the upper bound is
+ inclusive or exclusive and ``%upper_bound`` is always allowed to be equal to
+ ``%lower_bound``. ``%lower_bound == %upper_bound`` and
+ ``%inclusive`` being false implies an empty range, while
+ ``%lower_bound == %upper_bound - 1`` and ``%inclusive`` being true implies an
+ empty range.
``"separate_storage"(ptr %p1, ptr %p2)``
This indicates that no pointer :ref:`based <pointeraliasing>` on one of its
>From 534100869fd0297ed949c847f41c9b07ed371495 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 16 Jun 2026 00:13:43 +0200
Subject: [PATCH 5/5] Save text
---
llvm/docs/LangRef.rst | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index d9b7ceec96144..c6458f204eb0c 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -3190,10 +3190,9 @@ The following attributes are currently accepted:
Equivalent to :ref:`range(iN %lower_bound, %upper_bound) <attr_range>` on
``%val``, except that ``%inclusive`` determines whether the upper bound is
inclusive or exclusive and ``%upper_bound`` is always allowed to be equal to
- ``%lower_bound``. ``%lower_bound == %upper_bound`` and
- ``%inclusive`` being false implies an empty range, while
- ``%lower_bound == %upper_bound - 1`` and ``%inclusive`` being true implies an
- empty range.
+ ``%lower_bound``. ``%lower_bound == %upper_bound`` and ``%inclusive`` being
+ false implies an empty range, while ``%lower_bound == %upper_bound - 1`` and
+ ``%inclusive`` being true implies a full range.
``"separate_storage"(ptr %p1, ptr %p2)``
This indicates that no pointer :ref:`based <pointeraliasing>` on one of its
More information about the llvm-commits
mailing list