[llvm] [ArrayRef] Make iterator_range constructor const-agnostic (PR #205183)
Ramkumar Ramachandra via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 3 07:39:34 PDT 2026
https://github.com/artagnon updated https://github.com/llvm/llvm-project/pull/205183
>From 8ef9b3b92ce0e256e70a9c08a89632aa4211f2b9 Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Mon, 22 Jun 2026 20:21:19 +0100
Subject: [PATCH 1/3] [ArrayRef] Relax iterator_range constructor (NFC)
The enable_if guard is too restrictive, and stripping it does not lead
to worse compile-time errors on modern C++ compilers, or incorrect
results. The compiler would automatically check that the Data member is
initializable with the type T *const *.
---
llvm/include/llvm/ADT/ArrayRef.h | 9 +--
llvm/lib/Transforms/Vectorize/VPlan.h | 3 +
.../lib/Transforms/Vectorize/VPlanRecipes.cpp | 8 +-
llvm/unittests/ADT/ArrayRefTest.cpp | 77 +++++++++----------
4 files changed, 45 insertions(+), 52 deletions(-)
diff --git a/llvm/include/llvm/ADT/ArrayRef.h b/llvm/include/llvm/ADT/ArrayRef.h
index 6f12564c00c1e..e9552fe45d9b2 100644
--- a/llvm/include/llvm/ADT/ArrayRef.h
+++ b/llvm/include/llvm/ADT/ArrayRef.h
@@ -114,12 +114,9 @@ template <typename T> class LLVM_GSL_POINTER [[nodiscard]] ArrayRef {
#pragma GCC diagnostic pop
#endif
- /// Construct an ArrayRef<T> from iterator_range<U*>. This uses SFINAE
- /// to ensure that this is only used for iterator ranges over plain pointer
- /// iterators.
- template <typename U, typename = std::enable_if_t<
- std::is_convertible_v<U *const *, T *const *>>>
- ArrayRef(const iterator_range<U *> &Range)
+ /// Construct an ArrayRef from an iterator_range.
+ template <typename U>
+ ArrayRef(iterator_range<U> &&Range)
: Data(Range.begin()), Length(llvm::size(Range)) {}
/// @}
diff --git a/llvm/lib/Transforms/Vectorize/VPlan.h b/llvm/lib/Transforms/Vectorize/VPlan.h
index 3e7be2d67733a..0e6ff4ffdd3be 100644
--- a/llvm/lib/Transforms/Vectorize/VPlan.h
+++ b/llvm/lib/Transforms/Vectorize/VPlan.h
@@ -3468,6 +3468,9 @@ class LLVM_ABI_FOR_TEST VPReplicateRecipe : public VPRecipeWithIRFlags,
operand_range operandsWithoutMask() {
return isPredicated() ? drop_end(operands()) : operands();
}
+ const_operand_range operandsWithoutMask() const {
+ return isPredicated() ? drop_end(operands()) : operands();
+ }
/// Returns the number of operands, excluding the mask if the recipe is
/// predicated.
diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index 24abcf7646066..b8d7055b22de9 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -2308,8 +2308,7 @@ InstructionCost VPWidenIntrinsicRecipe::computeCallCost(
InstructionCost VPWidenIntrinsicRecipe::computeCost(ElementCount VF,
VPCostContext &Ctx) const {
- SmallVector<const VPValue *> ArgOps(operands());
- return computeCallCost(VectorIntrinsicID, ArgOps, *this, VF, Ctx);
+ return computeCallCost(VectorIntrinsicID, operands(), *this, VF, Ctx);
}
StringRef VPWidenIntrinsicRecipe::getIntrinsicName() const {
@@ -3804,9 +3803,8 @@ InstructionCost VPReplicateRecipe::computeCost(ElementCount VF,
auto *CalledFn =
cast<Function>(getOperand(getNumOperands() - 1)->getLiveInIRValue());
Type *ResultTy = this->getScalarType();
- SmallVector<const VPValue *> ArgOps(drop_end(operands()));
- return computeCallCost(CalledFn, ResultTy, ArgOps, isSingleScalar(), VF,
- Ctx);
+ return computeCallCost(CalledFn, ResultTy, operandsWithoutMask(),
+ isSingleScalar(), VF, Ctx);
}
case Instruction::Add:
case Instruction::Sub:
diff --git a/llvm/unittests/ADT/ArrayRefTest.cpp b/llvm/unittests/ADT/ArrayRefTest.cpp
index 622616f8144f1..77fa4b912d325 100644
--- a/llvm/unittests/ADT/ArrayRefTest.cpp
+++ b/llvm/unittests/ADT/ArrayRefTest.cpp
@@ -317,47 +317,6 @@ TEST(ArrayRefTest, ArrayRefFromStdArray) {
}
}
-struct TestRandomAccessIterator {
- using iterator_category = std::random_access_iterator_tag;
-};
-
-static_assert(!std::is_constructible_v<
- ArrayRef<int>, iterator_range<TestRandomAccessIterator>>,
- "cannot construct from iterator range with non-pointer iterator");
-static_assert(!std::is_constructible_v<ArrayRef<int>, iterator_range<int>>,
- "cannot construct from iterator range with non-pointer iterator");
-
-class TestBase {};
-
-class TestDerived : public TestBase {};
-
-static_assert(
- !std::is_constructible_v<ArrayRef<TestDerived>, iterator_range<TestBase *>>,
- "cannot construct ArrayRef with derived type");
-static_assert(
- !std::is_constructible_v<ArrayRef<TestBase>, iterator_range<TestDerived *>>,
- "cannot construct ArrayRef base type");
-static_assert(!std::is_constructible_v<ArrayRef<TestBase *>,
- iterator_range<TestDerived **>>,
- "cannot construct ArrayRef pointer of base type");
-static_assert(!std::is_constructible_v<MutableArrayRef<TestBase>,
- iterator_range<TestDerived *>>,
- "cannot construct MutableArrayRef base type");
-static_assert(!std::is_constructible_v<MutableArrayRef<TestBase *>,
- iterator_range<TestDerived **>>,
- "cannot construct MutableArrayRef pointer of base type");
-
-static_assert(
- !std::is_constructible_v<ArrayRef<int>, iterator_range<const int *>>,
- "cannot construct ArrayRef with non-const elements from const iterator "
- "range");
-static_assert(
- std::is_constructible_v<ArrayRef<char *>, iterator_range<char **>>,
- "should be able to construct ArrayRef from iterator_range over pointers");
-static_assert(
- !std::is_constructible_v<ArrayRef<char *>, iterator_range<char *const *>>,
- "should be able to construct ArrayRef from iterator_range over pointers");
-
TEST(ArrayRefTest, ArrayRefFromIteratorRange) {
int A1[] = {42, -5, 0, 1000000, -1000000, 0};
ArrayRef<int> A2 = make_range(&A1[0], &A1[5]);
@@ -372,8 +331,26 @@ TEST(ArrayRefTest, ArrayRefFromIteratorRange) {
EXPECT_EQ(A1[i], A3[i]);
}
+TEST(ArrayRefTest, ArrayConstRefIteratorConstRange) {
+ const int A1[] = {42, -5, 0, 1000000, -1000000, 0};
+ ArrayRef<const int> A2 = make_range(&A1[0], &A1[5]);
+
+ EXPECT_EQ(5ull, A2.size());
+ for (std::size_t i = 0; i < A2.size(); ++i)
+ EXPECT_EQ(A1[i], A2[i]);
+}
+
TEST(ArrayRefTest, ArrayRefFromIteratorConstRange) {
const int A1[] = {42, -5, 0, 1000000, -1000000, 0};
+ ArrayRef<int> A2 = make_range(&A1[0], &A1[5]);
+
+ EXPECT_EQ(5ull, A2.size());
+ for (std::size_t i = 0; i < A2.size(); ++i)
+ EXPECT_EQ(A1[i], A2[i]);
+}
+
+TEST(ArrayRefTest, ArrayConstRefFromIteratorRange) {
+ int A1[] = {42, -5, 0, 1000000, -1000000, 0};
ArrayRef<const int> A2 = make_range(&A1[0], &A1[5]);
EXPECT_EQ(5ull, A2.size());
@@ -381,6 +358,24 @@ TEST(ArrayRefTest, ArrayRefFromIteratorConstRange) {
EXPECT_EQ(A1[i], A2[i]);
}
+struct TestBase {
+ int M;
+ TestBase(int M) : M(M) {}
+};
+
+struct TestDerived : public TestBase {
+ TestDerived(int M) : TestBase(M) {}
+};
+
+TEST(ArrayRefTest, ArrayBaseRefFromIteratorDerivedRange) {
+ TestDerived A1[] = {42, -5, 0, 1000000, -1000000, 0};
+ ArrayRef<TestBase> A2 = make_range(&A1[0], &A1[5]);
+
+ EXPECT_EQ(5ull, A2.size());
+ for (std::size_t i = 0; i < A2.size(); ++i)
+ EXPECT_EQ(A1[i].M, A2[i].M);
+}
+
static_assert(std::is_trivially_copyable_v<ArrayRef<int>>,
"trivially copyable");
>From 0ff050c95774e89787ca15f43d471ecf04cf970e Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Mon, 22 Jun 2026 22:15:43 +0100
Subject: [PATCH 2/3] [ArrayRef] Avoid rvalue; attempt mlir build-fix
---
llvm/include/llvm/ADT/ArrayRef.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/include/llvm/ADT/ArrayRef.h b/llvm/include/llvm/ADT/ArrayRef.h
index e9552fe45d9b2..2dc7e463006e1 100644
--- a/llvm/include/llvm/ADT/ArrayRef.h
+++ b/llvm/include/llvm/ADT/ArrayRef.h
@@ -116,7 +116,7 @@ template <typename T> class LLVM_GSL_POINTER [[nodiscard]] ArrayRef {
/// Construct an ArrayRef from an iterator_range.
template <typename U>
- ArrayRef(iterator_range<U> &&Range)
+ ArrayRef(const iterator_range<U *> &Range)
: Data(Range.begin()), Length(llvm::size(Range)) {}
/// @}
>From f394b90972060ee886deeab53e6370f7dc29e302 Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Wed, 24 Jun 2026 13:09:42 +0100
Subject: [PATCH 3/3] [ArrayRef] Re-instate enable_if, improving it; remove
VPlan changes
---
llvm/include/llvm/ADT/ArrayRef.h | 7 +-
llvm/lib/Transforms/Vectorize/VPlan.h | 3 -
.../lib/Transforms/Vectorize/VPlanRecipes.cpp | 8 +-
llvm/unittests/ADT/ArrayRefTest.cpp | 76 ++++++++++++-------
4 files changed, 59 insertions(+), 35 deletions(-)
diff --git a/llvm/include/llvm/ADT/ArrayRef.h b/llvm/include/llvm/ADT/ArrayRef.h
index 2dc7e463006e1..ed91543e45bf1 100644
--- a/llvm/include/llvm/ADT/ArrayRef.h
+++ b/llvm/include/llvm/ADT/ArrayRef.h
@@ -114,8 +114,11 @@ template <typename T> class LLVM_GSL_POINTER [[nodiscard]] ArrayRef {
#pragma GCC diagnostic pop
#endif
- /// Construct an ArrayRef from an iterator_range.
- template <typename U>
+ /// Construct an ArrayRef<T> from iterator_range<U*>. This uses SFINAE
+ /// to ensure that this is only used for iterator ranges over plain pointer
+ /// iterators.
+ template <typename U, typename = std::enable_if_t<std::is_convertible_v<
+ U *const *, std::add_const_t<T> *const *>>>
ArrayRef(const iterator_range<U *> &Range)
: Data(Range.begin()), Length(llvm::size(Range)) {}
diff --git a/llvm/lib/Transforms/Vectorize/VPlan.h b/llvm/lib/Transforms/Vectorize/VPlan.h
index 0e6ff4ffdd3be..3e7be2d67733a 100644
--- a/llvm/lib/Transforms/Vectorize/VPlan.h
+++ b/llvm/lib/Transforms/Vectorize/VPlan.h
@@ -3468,9 +3468,6 @@ class LLVM_ABI_FOR_TEST VPReplicateRecipe : public VPRecipeWithIRFlags,
operand_range operandsWithoutMask() {
return isPredicated() ? drop_end(operands()) : operands();
}
- const_operand_range operandsWithoutMask() const {
- return isPredicated() ? drop_end(operands()) : operands();
- }
/// Returns the number of operands, excluding the mask if the recipe is
/// predicated.
diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index b8d7055b22de9..24abcf7646066 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -2308,7 +2308,8 @@ InstructionCost VPWidenIntrinsicRecipe::computeCallCost(
InstructionCost VPWidenIntrinsicRecipe::computeCost(ElementCount VF,
VPCostContext &Ctx) const {
- return computeCallCost(VectorIntrinsicID, operands(), *this, VF, Ctx);
+ SmallVector<const VPValue *> ArgOps(operands());
+ return computeCallCost(VectorIntrinsicID, ArgOps, *this, VF, Ctx);
}
StringRef VPWidenIntrinsicRecipe::getIntrinsicName() const {
@@ -3803,8 +3804,9 @@ InstructionCost VPReplicateRecipe::computeCost(ElementCount VF,
auto *CalledFn =
cast<Function>(getOperand(getNumOperands() - 1)->getLiveInIRValue());
Type *ResultTy = this->getScalarType();
- return computeCallCost(CalledFn, ResultTy, operandsWithoutMask(),
- isSingleScalar(), VF, Ctx);
+ SmallVector<const VPValue *> ArgOps(drop_end(operands()));
+ return computeCallCost(CalledFn, ResultTy, ArgOps, isSingleScalar(), VF,
+ Ctx);
}
case Instruction::Add:
case Instruction::Sub:
diff --git a/llvm/unittests/ADT/ArrayRefTest.cpp b/llvm/unittests/ADT/ArrayRefTest.cpp
index 77fa4b912d325..f99168df0670c 100644
--- a/llvm/unittests/ADT/ArrayRefTest.cpp
+++ b/llvm/unittests/ADT/ArrayRefTest.cpp
@@ -317,6 +317,55 @@ TEST(ArrayRefTest, ArrayRefFromStdArray) {
}
}
+struct TestRandomAccessIterator {
+ using iterator_category = std::random_access_iterator_tag;
+};
+
+static_assert(!std::is_constructible_v<
+ ArrayRef<int>, iterator_range<TestRandomAccessIterator>>,
+ "cannot construct from iterator range with non-pointer iterator");
+static_assert(!std::is_constructible_v<ArrayRef<int>, iterator_range<int>>,
+ "cannot construct from iterator range with non-pointer iterator");
+
+class TestBase {};
+
+class TestDerived : public TestBase {};
+
+static_assert(
+ !std::is_constructible_v<ArrayRef<TestDerived>, iterator_range<TestBase *>>,
+ "cannot construct ArrayRef with derived type");
+static_assert(
+ !std::is_constructible_v<ArrayRef<TestBase>, iterator_range<TestDerived *>>,
+ "cannot construct ArrayRef base type");
+static_assert(!std::is_constructible_v<ArrayRef<TestBase *>,
+ iterator_range<TestDerived **>>,
+ "cannot construct ArrayRef pointer of base type");
+static_assert(!std::is_constructible_v<MutableArrayRef<TestBase>,
+ iterator_range<TestDerived *>>,
+ "cannot construct MutableArrayRef base type");
+static_assert(!std::is_constructible_v<MutableArrayRef<TestBase *>,
+ iterator_range<TestDerived **>>,
+ "cannot construct MutableArrayRef pointer of base type");
+
+static_assert(
+ std::is_constructible_v<ArrayRef<int>, iterator_range<const int *>>,
+ "should be able to construct ArrayRef with non-const elements from const "
+ "iterator_range");
+static_assert(
+ std::is_constructible_v<ArrayRef<const int>, iterator_range<int *>>,
+ "should be able to construct ArrayRef with const elements from non-const "
+ "iterator_range");
+static_assert(
+ std::is_constructible_v<ArrayRef<const int>, iterator_range<const int *>>,
+ "should be able to construct ArrayRef with const elements from const "
+ "iterator_range");
+static_assert(
+ std::is_constructible_v<ArrayRef<char *>, iterator_range<char **>>,
+ "should be able to construct ArrayRef from iterator_range over pointers");
+static_assert(
+ std::is_constructible_v<ArrayRef<char *>, iterator_range<char *const *>>,
+ "should be able to construct ArrayRef from iterator_range over pointers");
+
TEST(ArrayRefTest, ArrayRefFromIteratorRange) {
int A1[] = {42, -5, 0, 1000000, -1000000, 0};
ArrayRef<int> A2 = make_range(&A1[0], &A1[5]);
@@ -349,33 +398,6 @@ TEST(ArrayRefTest, ArrayRefFromIteratorConstRange) {
EXPECT_EQ(A1[i], A2[i]);
}
-TEST(ArrayRefTest, ArrayConstRefFromIteratorRange) {
- int A1[] = {42, -5, 0, 1000000, -1000000, 0};
- ArrayRef<const int> A2 = make_range(&A1[0], &A1[5]);
-
- EXPECT_EQ(5ull, A2.size());
- for (std::size_t i = 0; i < A2.size(); ++i)
- EXPECT_EQ(A1[i], A2[i]);
-}
-
-struct TestBase {
- int M;
- TestBase(int M) : M(M) {}
-};
-
-struct TestDerived : public TestBase {
- TestDerived(int M) : TestBase(M) {}
-};
-
-TEST(ArrayRefTest, ArrayBaseRefFromIteratorDerivedRange) {
- TestDerived A1[] = {42, -5, 0, 1000000, -1000000, 0};
- ArrayRef<TestBase> A2 = make_range(&A1[0], &A1[5]);
-
- EXPECT_EQ(5ull, A2.size());
- for (std::size_t i = 0; i < A2.size(); ++i)
- EXPECT_EQ(A1[i].M, A2[i].M);
-}
-
static_assert(std::is_trivially_copyable_v<ArrayRef<int>>,
"trivially copyable");
More information about the llvm-commits
mailing list