[llvm] [TargetParser][NFC] Make FeatureBitset iterable (PR #206394)
Mark Zhuang via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 29 05:21:06 PDT 2026
https://github.com/zqb-all updated https://github.com/llvm/llvm-project/pull/206394
>From d8757c273a932f0cd9d0608e8ad4088433ebddb0 Mon Sep 17 00:00:00 2001
From: Mark Zhuang <mark.zhuang at spacemit.com>
Date: Mon, 29 Jun 2026 10:42:20 +0800
Subject: [PATCH 1/2] [TargetParser][NFC] Make FeatureBitset iterable
Add a const iterator to FeatureBitset that yields, for each bit
position, whether it is set, so callers can use llvm::enumerate
and a range-based loop instead of an index loop.
Assisted-by: claude-opus
---
.../llvm/TargetParser/SubtargetFeature.h | 27 +++++++++++++++++++
.../AArch64/AsmParser/AArch64AsmParser.cpp | 6 ++---
.../lib/Target/ARM/AsmParser/ARMAsmParser.cpp | 6 ++---
.../Target/CSKY/AsmParser/CSKYAsmParser.cpp | 6 ++---
.../AsmParser/LoongArchAsmParser.cpp | 6 ++---
.../Target/RISCV/AsmParser/RISCVAsmParser.cpp | 6 ++---
.../lib/Target/X86/AsmParser/X86AsmParser.cpp | 7 +++--
.../TargetParser/TargetParserTest.cpp | 24 +++++++++++++++++
8 files changed, 69 insertions(+), 19 deletions(-)
diff --git a/llvm/include/llvm/TargetParser/SubtargetFeature.h b/llvm/include/llvm/TargetParser/SubtargetFeature.h
index a48b18745352a..a05a883da7d7f 100644
--- a/llvm/include/llvm/TargetParser/SubtargetFeature.h
+++ b/llvm/include/llvm/TargetParser/SubtargetFeature.h
@@ -20,6 +20,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/iterator.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/MathExtras.h"
#include <array>
@@ -84,6 +85,32 @@ class FeatureBitset {
constexpr size_t size() const { return MAX_SUBTARGET_FEATURES; }
+ /// Iterates over every bit position, yielding whether it is set. Pair with
+ /// llvm::enumerate to recover the feature index.
+ class const_iterator
+ : public iterator_facade_base<const_iterator, std::forward_iterator_tag,
+ bool, std::ptrdiff_t, const bool *, bool> {
+ const FeatureBitset *Parent = nullptr;
+ unsigned Index = 0;
+
+ public:
+ const_iterator() = default;
+ const_iterator(const FeatureBitset &Parent, unsigned Index)
+ : Parent(&Parent), Index(Index) {}
+
+ bool operator*() const { return (*Parent)[Index]; }
+ const_iterator &operator++() {
+ ++Index;
+ return *this;
+ }
+ bool operator==(const const_iterator &RHS) const {
+ return Index == RHS.Index;
+ }
+ };
+
+ const_iterator begin() const { return const_iterator(*this, 0); }
+ const_iterator end() const { return const_iterator(*this, size()); }
+
bool any() const {
return llvm::any_of(Bits, [](uint64_t I) { return I != 0; });
}
diff --git a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
index b3fda2fe849fa..61dc24b55de89 100644
--- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
+++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
@@ -6998,10 +6998,10 @@ bool AArch64AsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
// Special case the error message for the very common case where only
// a single subtarget feature is missing (neon, e.g.).
std::string Msg = "instruction requires:";
- for (unsigned i = 0, e = MissingFeatures.size(); i != e; ++i) {
- if (MissingFeatures[i]) {
+ for (auto [Index, IsSet] : enumerate(MissingFeatures)) {
+ if (IsSet) {
Msg += " ";
- Msg += getSubtargetFeatureName(i);
+ Msg += getSubtargetFeatureName(Index);
}
}
return Error(IDLoc, Msg);
diff --git a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
index 1cac96a744733..9172ab8e8d474 100644
--- a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
+++ b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
@@ -12857,9 +12857,9 @@ ARMAsmParser::FilterNearMisses(SmallVectorImpl<NearMissInfo> &NearMissesIn,
raw_svector_ostream OS(Message.Message);
OS << "instruction requires:";
- for (unsigned i = 0, e = MissingFeatures.size(); i != e; ++i)
- if (MissingFeatures.test(i))
- OS << ' ' << getSubtargetFeatureName(i);
+ for (auto [Index, IsSet] : enumerate(MissingFeatures))
+ if (IsSet)
+ OS << ' ' << getSubtargetFeatureName(Index);
NearMissesOut.emplace_back(Message);
diff --git a/llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp b/llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp
index 1feb24999b504..545dd881e8dad 100644
--- a/llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp
+++ b/llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp
@@ -675,10 +675,10 @@ bool CSKYAsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
assert(MissingFeatures.any() && "Unknown missing features!");
ListSeparator LS;
std::string Msg = "instruction requires the following: ";
- for (unsigned i = 0, e = MissingFeatures.size(); i != e; ++i) {
- if (MissingFeatures[i]) {
+ for (auto [Index, IsSet] : enumerate(MissingFeatures)) {
+ if (IsSet) {
Msg += LS;
- Msg += getSubtargetFeatureName(i);
+ Msg += getSubtargetFeatureName(Index);
}
}
return Error(IDLoc, Msg);
diff --git a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
index 5dbd849bdbfe3..f366f244679fb 100644
--- a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
+++ b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
@@ -1795,10 +1795,10 @@ bool LoongArchAsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
assert(MissingFeatures.any() && "Unknown missing features!");
bool FirstFeature = true;
std::string Msg = "instruction requires the following:";
- for (unsigned i = 0, e = MissingFeatures.size(); i != e; ++i) {
- if (MissingFeatures[i]) {
+ for (auto [Index, IsSet] : enumerate(MissingFeatures)) {
+ if (IsSet) {
Msg += FirstFeature ? " " : ", ";
- Msg += getSubtargetFeatureName(i);
+ Msg += getSubtargetFeatureName(Index);
FirstFeature = false;
}
}
diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
index 384efaae0a025..a946afef4f9c9 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -1483,10 +1483,10 @@ bool RISCVAsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
assert(MissingFeatures.any() && "Unknown missing features!");
bool FirstFeature = true;
std::string Msg = "instruction requires the following:";
- for (unsigned i = 0, e = MissingFeatures.size(); i != e; ++i) {
- if (MissingFeatures[i]) {
+ for (auto [Index, IsSet] : enumerate(MissingFeatures)) {
+ if (IsSet) {
Msg += FirstFeature ? " " : ", ";
- Msg += getSubtargetFeatureName(i);
+ Msg += getSubtargetFeatureName(Index);
FirstFeature = false;
}
}
diff --git a/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp b/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp
index 45aff90a9998c..2cfa495d53a0a 100644
--- a/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp
+++ b/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp
@@ -4314,10 +4314,9 @@ bool X86AsmParser::ErrorMissingFeature(SMLoc IDLoc,
SmallString<126> Msg;
raw_svector_ostream OS(Msg);
OS << "instruction requires:";
- for (unsigned i = 0, e = MissingFeatures.size(); i != e; ++i) {
- if (MissingFeatures[i])
- OS << ' ' << getSubtargetFeatureName(i);
- }
+ for (auto [Index, IsSet] : enumerate(MissingFeatures))
+ if (IsSet)
+ OS << ' ' << getSubtargetFeatureName(Index);
return Error(IDLoc, OS.str(), SMRange(), MatchingInlineAsm);
}
diff --git a/llvm/unittests/TargetParser/TargetParserTest.cpp b/llvm/unittests/TargetParser/TargetParserTest.cpp
index 61da68940d9a1..070a88b918052 100644
--- a/llvm/unittests/TargetParser/TargetParserTest.cpp
+++ b/llvm/unittests/TargetParser/TargetParserTest.cpp
@@ -17,6 +17,7 @@
#include "llvm/TargetParser/AArch64TargetParser.h"
#include "llvm/TargetParser/ARMTargetParser.h"
#include "llvm/TargetParser/ARMTargetParserCommon.h"
+#include "llvm/TargetParser/SubtargetFeature.h"
#include "llvm/TargetParser/Triple.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@@ -535,6 +536,29 @@ INSTANTIATE_TEST_SUITE_P(
static constexpr unsigned NumARMCPUArchs = 95;
+TEST(FeatureBitsetTest, Iterator) {
+ // Empty bitset: every position is visited and reports false.
+ FeatureBitset Empty;
+ unsigned Count = 0;
+ for (bool IsSet : Empty) {
+ EXPECT_FALSE(IsSet);
+ ++Count;
+ }
+ EXPECT_EQ(Count, Empty.size());
+
+ // enumerate recovers the set feature indices.
+ FeatureBitset Bits;
+ Bits.set(0).set(5).set(63).set(64).set(200);
+ std::vector<unsigned> SetIndices;
+ for (auto [Index, IsSet] : enumerate(Bits))
+ if (IsSet)
+ SetIndices.push_back(Index);
+ EXPECT_EQ(SetIndices, (std::vector<unsigned>{0, 5, 63, 64, 200}));
+
+ for (auto [Index, IsSet] : enumerate(Bits))
+ EXPECT_EQ(IsSet, Bits[Index]);
+}
+
TEST(TargetParserTest, testARMCPUArchList) {
SmallVector<StringRef, NumARMCPUArchs> List;
ARM::fillValidCPUArchList(List);
>From 3ddc3aee46d76b58699f5b64fc41a3785e38ef85 Mon Sep 17 00:00:00 2001
From: Mark Zhuang <mark.zhuang at spacemit.com>
Date: Mon, 29 Jun 2026 18:43:25 +0800
Subject: [PATCH 2/2] [TargetParser][NFC] Make FeatureBitset iterate over set
bits
begin()/end() now return only set bits and give the index directly,
skipping unset bits with countr_zero. Callers can write
`for (unsigned Index : Bits)` instead of using llvm::enumerate.
Assisted-by: claude-opus
---
.../llvm/TargetParser/SubtargetFeature.h | 25 +++++++++++----
.../AArch64/AsmParser/AArch64AsmParser.cpp | 8 ++---
.../lib/Target/ARM/AsmParser/ARMAsmParser.cpp | 5 ++-
.../Target/CSKY/AsmParser/CSKYAsmParser.cpp | 8 ++---
.../AsmParser/LoongArchAsmParser.cpp | 10 +++---
.../Target/RISCV/AsmParser/RISCVAsmParser.cpp | 10 +++---
.../lib/Target/X86/AsmParser/X86AsmParser.cpp | 5 ++-
.../TargetParser/TargetParserTest.cpp | 31 ++++++++++++-------
8 files changed, 56 insertions(+), 46 deletions(-)
diff --git a/llvm/include/llvm/TargetParser/SubtargetFeature.h b/llvm/include/llvm/TargetParser/SubtargetFeature.h
index a05a883da7d7f..3c136b27374dd 100644
--- a/llvm/include/llvm/TargetParser/SubtargetFeature.h
+++ b/llvm/include/llvm/TargetParser/SubtargetFeature.h
@@ -85,11 +85,22 @@ class FeatureBitset {
constexpr size_t size() const { return MAX_SUBTARGET_FEATURES; }
- /// Iterates over every bit position, yielding whether it is set. Pair with
- /// llvm::enumerate to recover the feature index.
+ /// Index of the first set bit at or after Begin, or size() if none.
+ unsigned find_first_from(unsigned Begin) const {
+ for (unsigned Word = Begin / 64; Word < Bits.size(); ++Word) {
+ uint64_t Masked = Bits[Word] & maskTrailingZeros<uint64_t>(Begin % 64);
+ if (Masked)
+ return Word * 64 + llvm::countr_zero(Masked);
+ Begin = (Word + 1) * 64;
+ }
+ return size();
+ }
+
+ /// Yields the index of each set bit, skipping unset bits via countr_zero.
class const_iterator
: public iterator_facade_base<const_iterator, std::forward_iterator_tag,
- bool, std::ptrdiff_t, const bool *, bool> {
+ const unsigned, std::ptrdiff_t,
+ const unsigned *, unsigned> {
const FeatureBitset *Parent = nullptr;
unsigned Index = 0;
@@ -98,9 +109,9 @@ class FeatureBitset {
const_iterator(const FeatureBitset &Parent, unsigned Index)
: Parent(&Parent), Index(Index) {}
- bool operator*() const { return (*Parent)[Index]; }
+ unsigned operator*() const { return Index; }
const_iterator &operator++() {
- ++Index;
+ Index = Parent->find_first_from(Index + 1);
return *this;
}
bool operator==(const const_iterator &RHS) const {
@@ -108,7 +119,9 @@ class FeatureBitset {
}
};
- const_iterator begin() const { return const_iterator(*this, 0); }
+ const_iterator begin() const {
+ return const_iterator(*this, find_first_from(0));
+ }
const_iterator end() const { return const_iterator(*this, size()); }
bool any() const {
diff --git a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
index 61dc24b55de89..27129659b2b69 100644
--- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
+++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
@@ -6998,11 +6998,9 @@ bool AArch64AsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
// Special case the error message for the very common case where only
// a single subtarget feature is missing (neon, e.g.).
std::string Msg = "instruction requires:";
- for (auto [Index, IsSet] : enumerate(MissingFeatures)) {
- if (IsSet) {
- Msg += " ";
- Msg += getSubtargetFeatureName(Index);
- }
+ for (unsigned Index : MissingFeatures) {
+ Msg += " ";
+ Msg += getSubtargetFeatureName(Index);
}
return Error(IDLoc, Msg);
}
diff --git a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
index 9172ab8e8d474..c07a41cf79a80 100644
--- a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
+++ b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
@@ -12857,9 +12857,8 @@ ARMAsmParser::FilterNearMisses(SmallVectorImpl<NearMissInfo> &NearMissesIn,
raw_svector_ostream OS(Message.Message);
OS << "instruction requires:";
- for (auto [Index, IsSet] : enumerate(MissingFeatures))
- if (IsSet)
- OS << ' ' << getSubtargetFeatureName(Index);
+ for (unsigned Index : MissingFeatures)
+ OS << ' ' << getSubtargetFeatureName(Index);
NearMissesOut.emplace_back(Message);
diff --git a/llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp b/llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp
index 545dd881e8dad..152d2ee8918ae 100644
--- a/llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp
+++ b/llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp
@@ -675,11 +675,9 @@ bool CSKYAsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
assert(MissingFeatures.any() && "Unknown missing features!");
ListSeparator LS;
std::string Msg = "instruction requires the following: ";
- for (auto [Index, IsSet] : enumerate(MissingFeatures)) {
- if (IsSet) {
- Msg += LS;
- Msg += getSubtargetFeatureName(Index);
- }
+ for (unsigned Index : MissingFeatures) {
+ Msg += LS;
+ Msg += getSubtargetFeatureName(Index);
}
return Error(IDLoc, Msg);
}
diff --git a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
index f366f244679fb..ee043f8279a27 100644
--- a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
+++ b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
@@ -1795,12 +1795,10 @@ bool LoongArchAsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
assert(MissingFeatures.any() && "Unknown missing features!");
bool FirstFeature = true;
std::string Msg = "instruction requires the following:";
- for (auto [Index, IsSet] : enumerate(MissingFeatures)) {
- if (IsSet) {
- Msg += FirstFeature ? " " : ", ";
- Msg += getSubtargetFeatureName(Index);
- FirstFeature = false;
- }
+ for (unsigned Index : MissingFeatures) {
+ Msg += FirstFeature ? " " : ", ";
+ Msg += getSubtargetFeatureName(Index);
+ FirstFeature = false;
}
return Error(IDLoc, Msg);
}
diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
index a946afef4f9c9..fc1c18f81ffb4 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -1483,12 +1483,10 @@ bool RISCVAsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
assert(MissingFeatures.any() && "Unknown missing features!");
bool FirstFeature = true;
std::string Msg = "instruction requires the following:";
- for (auto [Index, IsSet] : enumerate(MissingFeatures)) {
- if (IsSet) {
- Msg += FirstFeature ? " " : ", ";
- Msg += getSubtargetFeatureName(Index);
- FirstFeature = false;
- }
+ for (unsigned Index : MissingFeatures) {
+ Msg += FirstFeature ? " " : ", ";
+ Msg += getSubtargetFeatureName(Index);
+ FirstFeature = false;
}
return Error(IDLoc, Msg);
}
diff --git a/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp b/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp
index 2cfa495d53a0a..80bb82517cd1d 100644
--- a/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp
+++ b/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp
@@ -4314,9 +4314,8 @@ bool X86AsmParser::ErrorMissingFeature(SMLoc IDLoc,
SmallString<126> Msg;
raw_svector_ostream OS(Msg);
OS << "instruction requires:";
- for (auto [Index, IsSet] : enumerate(MissingFeatures))
- if (IsSet)
- OS << ' ' << getSubtargetFeatureName(Index);
+ for (unsigned Index : MissingFeatures)
+ OS << ' ' << getSubtargetFeatureName(Index);
return Error(IDLoc, OS.str(), SMRange(), MatchingInlineAsm);
}
diff --git a/llvm/unittests/TargetParser/TargetParserTest.cpp b/llvm/unittests/TargetParser/TargetParserTest.cpp
index 070a88b918052..cf0f5812d13f2 100644
--- a/llvm/unittests/TargetParser/TargetParserTest.cpp
+++ b/llvm/unittests/TargetParser/TargetParserTest.cpp
@@ -537,26 +537,33 @@ INSTANTIATE_TEST_SUITE_P(
static constexpr unsigned NumARMCPUArchs = 95;
TEST(FeatureBitsetTest, Iterator) {
- // Empty bitset: every position is visited and reports false.
+ // Empty bitset yields nothing.
FeatureBitset Empty;
- unsigned Count = 0;
- for (bool IsSet : Empty) {
- EXPECT_FALSE(IsSet);
- ++Count;
+ EXPECT_EQ(Empty.begin(), Empty.end());
+ for (unsigned Index : Empty) {
+ (void)Index;
+ FAIL() << "empty bitset should yield no set bits";
}
- EXPECT_EQ(Count, Empty.size());
- // enumerate recovers the set feature indices.
+ // Yields the set indices in order, crossing the 63/64 word boundary.
FeatureBitset Bits;
Bits.set(0).set(5).set(63).set(64).set(200);
std::vector<unsigned> SetIndices;
- for (auto [Index, IsSet] : enumerate(Bits))
- if (IsSet)
- SetIndices.push_back(Index);
+ for (unsigned Index : Bits)
+ SetIndices.push_back(Index);
EXPECT_EQ(SetIndices, (std::vector<unsigned>{0, 5, 63, 64, 200}));
- for (auto [Index, IsSet] : enumerate(Bits))
- EXPECT_EQ(IsSet, Bits[Index]);
+ // Every yielded index is set, and the count matches.
+ for (unsigned Index : Bits)
+ EXPECT_TRUE(Bits[Index]);
+ EXPECT_EQ(SetIndices.size(), Bits.count());
+
+ // The final position is reached.
+ FeatureBitset Last;
+ unsigned LastIndex = Last.size() - 1;
+ Last.set(LastIndex);
+ SetIndices.assign(Last.begin(), Last.end());
+ EXPECT_EQ(SetIndices, (std::vector<unsigned>{LastIndex}));
}
TEST(TargetParserTest, testARMCPUArchList) {
More information about the llvm-commits
mailing list