[clang] [NFC][Clang][FMV] Refactor sema checking of target_version/clones attributes. (PR #149067)
Alexandros Lamprineas via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 16 08:26:32 PDT 2025
https://github.com/labrinea updated https://github.com/llvm/llvm-project/pull/149067
>From 9a56f94c9851721c92dc4b026a33e44d3ebf3b20 Mon Sep 17 00:00:00 2001
From: Alexandros Lamprineas <alexandros.lamprineas at arm.com>
Date: Tue, 15 Jul 2025 14:57:57 +0100
Subject: [PATCH 1/2] [NFC][FMV] Refactor sema checking of
target_version/clones attributes.
Sema currently has checkTargetVersionAttr and checkTargetClonesAttrString
to diagnose the said attributes. However the code tries to handle all of
AArch64, RISC-V and X86 targets at once which is hard to maintain, therefore
I am splitting these functions. Unfortunately I could not use polymorphism
because all of Sema, SemaARM, SemaRISCV and SemaX86 inherit from SemaBase.
The Sema instance itself contains instances of every other target specific
Sema.
---
clang/include/clang/Sema/Sema.h | 7 -
clang/include/clang/Sema/SemaARM.h | 5 +
clang/include/clang/Sema/SemaRISCV.h | 5 +
clang/include/clang/Sema/SemaX86.h | 4 +
clang/lib/Sema/SemaARM.cpp | 87 ++++++++
clang/lib/Sema/SemaDeclAttr.cpp | 285 ++++-----------------------
clang/lib/Sema/SemaRISCV.cpp | 112 +++++++++++
clang/lib/Sema/SemaX86.cpp | 58 ++++++
8 files changed, 307 insertions(+), 256 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index b331acbe606b7..352dc42edf464 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -4922,13 +4922,6 @@ class Sema final : public SemaBase {
// handled later in the process, once we know how many exist.
bool checkTargetAttr(SourceLocation LiteralLoc, StringRef Str);
- /// Check Target Version attrs
- bool checkTargetVersionAttr(SourceLocation Loc, Decl *D, StringRef Str);
- bool checkTargetClonesAttrString(
- SourceLocation LiteralLoc, StringRef Str, const StringLiteral *Literal,
- Decl *D, bool &HasDefault, bool &HasCommas, bool &HasNotDefault,
- SmallVectorImpl<SmallString<64>> &StringsBuffer);
-
ErrorAttr *mergeErrorAttr(Decl *D, const AttributeCommonInfo &CI,
StringRef NewUserDiagnostic);
FormatAttr *mergeFormatAttr(Decl *D, const AttributeCommonInfo &CI,
diff --git a/clang/include/clang/Sema/SemaARM.h b/clang/include/clang/Sema/SemaARM.h
index 788a7abf5f9c1..4c2ca59dc929a 100644
--- a/clang/include/clang/Sema/SemaARM.h
+++ b/clang/include/clang/Sema/SemaARM.h
@@ -91,6 +91,11 @@ class SemaARM : public SemaBase {
/// Return true if the given vector types are lax-compatible SVE vector types,
/// false otherwise.
bool areLaxCompatibleSveTypes(QualType FirstType, QualType SecondType);
+
+ bool checkTargetVersionAttr(StringRef Str, SourceLocation Loc);
+ bool checkTargetClonesAttr(SmallVectorImpl<StringRef> &Strs,
+ SmallVectorImpl<SourceLocation> &Locs,
+ SmallVectorImpl<SmallString<64>> &Buffer);
};
SemaARM::ArmStreamingType getArmStreamingFnType(const FunctionDecl *FD);
diff --git a/clang/include/clang/Sema/SemaRISCV.h b/clang/include/clang/Sema/SemaRISCV.h
index 8d2e1c6b7512f..77e1f4a5894c3 100644
--- a/clang/include/clang/Sema/SemaRISCV.h
+++ b/clang/include/clang/Sema/SemaRISCV.h
@@ -55,6 +55,11 @@ class SemaRISCV : public SemaBase {
bool DeclareAndesVectorBuiltins = false;
std::unique_ptr<sema::RISCVIntrinsicManager> IntrinsicManager;
+
+ bool checkTargetVersionAttr(StringRef Str, SourceLocation Loc);
+ bool checkTargetClonesAttr(SmallVectorImpl<StringRef> &Strs,
+ SmallVectorImpl<SourceLocation> &Locs,
+ SmallVectorImpl<SmallString<64>> &Buffer);
};
std::unique_ptr<sema::RISCVIntrinsicManager>
diff --git a/clang/include/clang/Sema/SemaX86.h b/clang/include/clang/Sema/SemaX86.h
index b5a23f1bede04..bfe0b54ad1be9 100644
--- a/clang/include/clang/Sema/SemaX86.h
+++ b/clang/include/clang/Sema/SemaX86.h
@@ -37,6 +37,10 @@ class SemaX86 : public SemaBase {
void handleAnyInterruptAttr(Decl *D, const ParsedAttr &AL);
void handleForceAlignArgPointerAttr(Decl *D, const ParsedAttr &AL);
+
+ bool checkTargetClonesAttr(SmallVectorImpl<StringRef> &Strs,
+ SmallVectorImpl<SourceLocation> &Locs,
+ SmallVectorImpl<SmallString<64>> &Buffer);
};
} // namespace clang
diff --git a/clang/lib/Sema/SemaARM.cpp b/clang/lib/Sema/SemaARM.cpp
index bd603a925d15e..6c1917511b59a 100644
--- a/clang/lib/Sema/SemaARM.cpp
+++ b/clang/lib/Sema/SemaARM.cpp
@@ -1535,4 +1535,91 @@ bool SemaARM::areLaxCompatibleSveTypes(QualType FirstType,
IsLaxCompatible(SecondType, FirstType);
}
+enum FirstParam { Unsupported, Duplicate, Unknown };
+enum SecondParam { None, CPU, Tune };
+enum ThirdParam { Target, TargetClones, TargetVersion };
+
+bool SemaARM::checkTargetVersionAttr(StringRef Str, SourceLocation Loc) {
+ llvm::SmallVector<StringRef, 8> Features;
+ Str.split(Features, '+');
+ for (StringRef Feat : Features) {
+ Feat = Feat.trim();
+ if (Feat == "default")
+ continue;
+ if (!getASTContext().getTargetInfo().validateCpuSupports(Feat))
+ return Diag(Loc, diag::warn_unsupported_target_attribute)
+ << Unsupported << None << Feat << TargetVersion;
+ }
+ return false;
+}
+
+bool SemaARM::checkTargetClonesAttr(SmallVectorImpl<StringRef> &Strs,
+ SmallVectorImpl<SourceLocation> &Locs,
+ SmallVectorImpl<SmallString<64>> &Buffer) {
+ if (!getASTContext().getTargetInfo().hasFeature("fmv"))
+ return true;
+
+ assert(Strs.size() == Locs.size() &&
+ "Mismatch between number of strings and locations");
+
+ bool HasDefault = false;
+ bool HasNonDefault = false;
+ for (unsigned I = 0; I < Strs.size(); ++I) {
+ StringRef Str = Strs[I].trim();
+ SourceLocation Loc = Locs[I];
+
+ if (Str.empty())
+ return Diag(Loc, diag::warn_unsupported_target_attribute)
+ << Unsupported << None << "" << TargetClones;
+
+ if (Str == "default") {
+ if (HasDefault)
+ Diag(Loc, diag::warn_target_clone_duplicate_options);
+ else {
+ Buffer.push_back(Str);
+ HasDefault = true;
+ }
+ continue;
+ }
+
+ bool HasCodeGenImpact = false;
+ llvm::SmallVector<StringRef, 8> Features;
+ llvm::SmallVector<StringRef, 8> ValidFeatures;
+ Str.split(Features, '+');
+ for (StringRef Feat : Features) {
+ Feat = Feat.trim();
+ if (!getASTContext().getTargetInfo().validateCpuSupports(Feat)) {
+ Diag(Loc, diag::warn_unsupported_target_attribute)
+ << Unsupported << None << Feat << TargetClones;
+ continue;
+ }
+ if (getASTContext().getTargetInfo().doesFeatureAffectCodeGen(Feat))
+ HasCodeGenImpact = true;
+ ValidFeatures.push_back(Feat);
+ }
+ // Canonize TargetClones Attributes
+ SmallString<64> NewStr;
+ llvm::sort(ValidFeatures);
+ for (StringRef Feat : ValidFeatures) {
+ if (!NewStr.empty())
+ NewStr.append("+");
+ NewStr.append(Feat);
+ }
+ if (llvm::is_contained(Buffer, NewStr))
+ Diag(Loc, diag::warn_target_clone_duplicate_options);
+ else if (!HasCodeGenImpact)
+ // Ignore features in target_clone attribute that don't impact
+ // code generation
+ Diag(Loc, diag::warn_target_clone_no_impact_options);
+ else if (!NewStr.empty()) {
+ Buffer.push_back(NewStr);
+ HasNonDefault = true;
+ }
+ }
+ if (!HasNonDefault)
+ return true;
+
+ return false;
+}
+
} // namespace clang
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 5f481ed1f7139..3b0b549b70035 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -3331,78 +3331,20 @@ bool Sema::checkTargetAttr(SourceLocation LiteralLoc, StringRef AttrStr) {
return false;
}
-bool Sema::checkTargetVersionAttr(SourceLocation LiteralLoc, Decl *D,
- StringRef AttrStr) {
- enum FirstParam { Unsupported };
- enum SecondParam { None };
- enum ThirdParam { Target, TargetClones, TargetVersion };
- llvm::SmallVector<StringRef, 8> Features;
- if (Context.getTargetInfo().getTriple().isRISCV()) {
- llvm::SmallVector<StringRef, 8> AttrStrs;
- AttrStr.split(AttrStrs, ';');
-
- bool HasArch = false;
- bool HasPriority = false;
- bool HasDefault = false;
- bool DuplicateAttr = false;
- for (auto &AttrStr : AttrStrs) {
- // Only support arch=+ext,... syntax.
- if (AttrStr.starts_with("arch=+")) {
- if (HasArch)
- DuplicateAttr = true;
- HasArch = true;
- ParsedTargetAttr TargetAttr =
- Context.getTargetInfo().parseTargetAttr(AttrStr);
-
- if (TargetAttr.Features.empty() ||
- llvm::any_of(TargetAttr.Features, [&](const StringRef Ext) {
- return !RISCV().isValidFMVExtension(Ext);
- }))
- return Diag(LiteralLoc, diag::warn_unsupported_target_attribute)
- << Unsupported << None << AttrStr << TargetVersion;
- } else if (AttrStr.starts_with("default")) {
- if (HasDefault)
- DuplicateAttr = true;
- HasDefault = true;
- } else if (AttrStr.consume_front("priority=")) {
- if (HasPriority)
- DuplicateAttr = true;
- HasPriority = true;
- unsigned Digit;
- if (AttrStr.getAsInteger(0, Digit))
- return Diag(LiteralLoc, diag::warn_unsupported_target_attribute)
- << Unsupported << None << AttrStr << TargetVersion;
- } else {
- return Diag(LiteralLoc, diag::warn_unsupported_target_attribute)
- << Unsupported << None << AttrStr << TargetVersion;
- }
- }
-
- if (((HasPriority || HasArch) && HasDefault) || DuplicateAttr ||
- (HasPriority && !HasArch))
- return Diag(LiteralLoc, diag::warn_unsupported_target_attribute)
- << Unsupported << None << AttrStr << TargetVersion;
-
- return false;
- }
- AttrStr.split(Features, "+");
- for (auto &CurFeature : Features) {
- CurFeature = CurFeature.trim();
- if (CurFeature == "default")
- continue;
- if (!Context.getTargetInfo().validateCpuSupports(CurFeature))
- return Diag(LiteralLoc, diag::warn_unsupported_target_attribute)
- << Unsupported << None << CurFeature << TargetVersion;
- }
- return false;
-}
-
static void handleTargetVersionAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
StringRef Str;
- SourceLocation LiteralLoc;
- if (!S.checkStringLiteralArgumentAttr(AL, 0, Str, &LiteralLoc) ||
- S.checkTargetVersionAttr(LiteralLoc, D, Str))
+ SourceLocation Loc;
+ if (!S.checkStringLiteralArgumentAttr(AL, 0, Str, &Loc))
return;
+
+ if (S.Context.getTargetInfo().getTriple().isAArch64()) {
+ if (S.ARM().checkTargetVersionAttr(Str, Loc))
+ return;
+ } else if (S.Context.getTargetInfo().getTriple().isRISCV()) {
+ if (S.RISCV().checkTargetVersionAttr(Str, Loc))
+ return;
+ }
+
TargetVersionAttr *NewAttr =
::new (S.Context) TargetVersionAttr(S.Context, AL, Str);
D->addAttr(NewAttr);
@@ -3419,158 +3361,7 @@ static void handleTargetAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
D->addAttr(NewAttr);
}
-bool Sema::checkTargetClonesAttrString(
- SourceLocation LiteralLoc, StringRef Str, const StringLiteral *Literal,
- Decl *D, bool &HasDefault, bool &HasCommas, bool &HasNotDefault,
- SmallVectorImpl<SmallString<64>> &StringsBuffer) {
- enum FirstParam { Unsupported, Duplicate, Unknown };
- enum SecondParam { None, CPU, Tune };
- enum ThirdParam { Target, TargetClones };
- HasCommas = HasCommas || Str.contains(',');
- const TargetInfo &TInfo = Context.getTargetInfo();
- // Warn on empty at the beginning of a string.
- if (Str.size() == 0)
- return Diag(LiteralLoc, diag::warn_unsupported_target_attribute)
- << Unsupported << None << "" << TargetClones;
-
- std::pair<StringRef, StringRef> Parts = {{}, Str};
- while (!Parts.second.empty()) {
- Parts = Parts.second.split(',');
- StringRef Cur = Parts.first.trim();
- SourceLocation CurLoc =
- Literal->getLocationOfByte(Cur.data() - Literal->getString().data(),
- getSourceManager(), getLangOpts(), TInfo);
-
- bool DefaultIsDupe = false;
- bool HasCodeGenImpact = false;
- if (Cur.empty())
- return Diag(CurLoc, diag::warn_unsupported_target_attribute)
- << Unsupported << None << "" << TargetClones;
-
- if (TInfo.getTriple().isAArch64()) {
- // AArch64 target clones specific
- if (Cur == "default") {
- DefaultIsDupe = HasDefault;
- HasDefault = true;
- if (llvm::is_contained(StringsBuffer, Cur) || DefaultIsDupe)
- Diag(CurLoc, diag::warn_target_clone_duplicate_options);
- else
- StringsBuffer.push_back(Cur);
- } else {
- std::pair<StringRef, StringRef> CurParts = {{}, Cur};
- llvm::SmallVector<StringRef, 8> CurFeatures;
- while (!CurParts.second.empty()) {
- CurParts = CurParts.second.split('+');
- StringRef CurFeature = CurParts.first.trim();
- if (!TInfo.validateCpuSupports(CurFeature)) {
- Diag(CurLoc, diag::warn_unsupported_target_attribute)
- << Unsupported << None << CurFeature << TargetClones;
- continue;
- }
- if (TInfo.doesFeatureAffectCodeGen(CurFeature))
- HasCodeGenImpact = true;
- CurFeatures.push_back(CurFeature);
- }
- // Canonize TargetClones Attributes
- llvm::sort(CurFeatures);
- SmallString<64> Res;
- for (auto &CurFeat : CurFeatures) {
- if (!Res.empty())
- Res.append("+");
- Res.append(CurFeat);
- }
- if (llvm::is_contained(StringsBuffer, Res) || DefaultIsDupe)
- Diag(CurLoc, diag::warn_target_clone_duplicate_options);
- else if (!HasCodeGenImpact)
- // Ignore features in target_clone attribute that don't impact
- // code generation
- Diag(CurLoc, diag::warn_target_clone_no_impact_options);
- else if (!Res.empty()) {
- StringsBuffer.push_back(Res);
- HasNotDefault = true;
- }
- }
- } else if (TInfo.getTriple().isRISCV()) {
- // Suppress warn_target_clone_mixed_values
- HasCommas = false;
-
- // Cur is split's parts of Str. RISC-V uses Str directly,
- // so skip when encountered more than once.
- if (!Str.starts_with(Cur))
- continue;
-
- llvm::SmallVector<StringRef, 8> AttrStrs;
- Str.split(AttrStrs, ";");
-
- bool IsPriority = false;
- bool IsDefault = false;
- for (auto &AttrStr : AttrStrs) {
- // Only support arch=+ext,... syntax.
- if (AttrStr.starts_with("arch=+")) {
- ParsedTargetAttr TargetAttr =
- Context.getTargetInfo().parseTargetAttr(AttrStr);
-
- if (TargetAttr.Features.empty() ||
- llvm::any_of(TargetAttr.Features, [&](const StringRef Ext) {
- return !RISCV().isValidFMVExtension(Ext);
- }))
- return Diag(CurLoc, diag::warn_unsupported_target_attribute)
- << Unsupported << None << Str << TargetClones;
- } else if (AttrStr.starts_with("default")) {
- IsDefault = true;
- DefaultIsDupe = HasDefault;
- HasDefault = true;
- } else if (AttrStr.consume_front("priority=")) {
- IsPriority = true;
- unsigned Digit;
- if (AttrStr.getAsInteger(0, Digit))
- return Diag(CurLoc, diag::warn_unsupported_target_attribute)
- << Unsupported << None << Str << TargetClones;
- } else {
- return Diag(CurLoc, diag::warn_unsupported_target_attribute)
- << Unsupported << None << Str << TargetClones;
- }
- }
-
- if (IsPriority && IsDefault)
- return Diag(CurLoc, diag::warn_unsupported_target_attribute)
- << Unsupported << None << Str << TargetClones;
-
- if (llvm::is_contained(StringsBuffer, Str) || DefaultIsDupe)
- Diag(CurLoc, diag::warn_target_clone_duplicate_options);
- StringsBuffer.push_back(Str);
- } else {
- // Other targets ( currently X86 )
- if (Cur.starts_with("arch=")) {
- if (!Context.getTargetInfo().isValidCPUName(
- Cur.drop_front(sizeof("arch=") - 1)))
- return Diag(CurLoc, diag::warn_unsupported_target_attribute)
- << Unsupported << CPU << Cur.drop_front(sizeof("arch=") - 1)
- << TargetClones;
- } else if (Cur == "default") {
- DefaultIsDupe = HasDefault;
- HasDefault = true;
- } else if (!Context.getTargetInfo().isValidFeatureName(Cur) ||
- Context.getTargetInfo().getFMVPriority(Cur) == 0)
- return Diag(CurLoc, diag::warn_unsupported_target_attribute)
- << Unsupported << None << Cur << TargetClones;
- if (llvm::is_contained(StringsBuffer, Cur) || DefaultIsDupe)
- Diag(CurLoc, diag::warn_target_clone_duplicate_options);
- // Note: Add even if there are duplicates, since it changes name mangling.
- StringsBuffer.push_back(Cur);
- }
- }
- if (Str.rtrim().ends_with(","))
- return Diag(LiteralLoc, diag::warn_unsupported_target_attribute)
- << Unsupported << None << "" << TargetClones;
- return false;
-}
-
static void handleTargetClonesAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
- if (S.Context.getTargetInfo().getTriple().isAArch64() &&
- !S.Context.getTargetInfo().hasFeature("fmv"))
- return;
-
// Ensure we don't combine these with themselves, since that causes some
// confusing behavior.
if (const auto *Other = D->getAttr<TargetClonesAttr>()) {
@@ -3581,31 +3372,6 @@ static void handleTargetClonesAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
if (checkAttrMutualExclusion<TargetClonesAttr>(S, D, AL))
return;
- SmallVector<StringRef, 2> Strings;
- SmallVector<SmallString<64>, 2> StringsBuffer;
- bool HasCommas = false, HasDefault = false, HasNotDefault = false;
-
- for (unsigned I = 0, E = AL.getNumArgs(); I != E; ++I) {
- StringRef CurStr;
- SourceLocation LiteralLoc;
- if (!S.checkStringLiteralArgumentAttr(AL, I, CurStr, &LiteralLoc) ||
- S.checkTargetClonesAttrString(
- LiteralLoc, CurStr,
- cast<StringLiteral>(AL.getArgAsExpr(I)->IgnoreParenCasts()), D,
- HasDefault, HasCommas, HasNotDefault, StringsBuffer))
- return;
- }
- for (auto &SmallStr : StringsBuffer)
- Strings.push_back(SmallStr.str());
-
- if (HasCommas && AL.getNumArgs() > 1)
- S.Diag(AL.getLoc(), diag::warn_target_clone_mixed_values);
-
- if (!HasDefault && !S.Context.getTargetInfo().getTriple().isAArch64()) {
- S.Diag(AL.getLoc(), diag::err_target_clone_must_have_default);
- return;
- }
-
// FIXME: We could probably figure out how to get this to work for lambdas
// someday.
if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
@@ -3617,11 +3383,32 @@ static void handleTargetClonesAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
}
}
- // No multiversion if we have default version only.
- if (S.Context.getTargetInfo().getTriple().isAArch64() && !HasNotDefault)
- return;
+ SmallVector<StringRef, 2> Strings;
+ SmallVector<SourceLocation, 2> Locations;
+ for (unsigned I = 0, E = AL.getNumArgs(); I != E; ++I) {
+ StringRef Str;
+ SourceLocation Loc;
+ if (!S.checkStringLiteralArgumentAttr(AL, I, Str, &Loc))
+ return;
+ Strings.push_back(Str);
+ Locations.push_back(Loc);
+ }
+
+ SmallVector<SmallString<64>, 2> Buffer;
+ if (S.Context.getTargetInfo().getTriple().isAArch64()) {
+ if (S.ARM().checkTargetClonesAttr(Strings, Locations, Buffer))
+ return;
+ } else if (S.Context.getTargetInfo().getTriple().isRISCV()) {
+ if (S.RISCV().checkTargetClonesAttr(Strings, Locations, Buffer))
+ return;
+ } else if (S.Context.getTargetInfo().getTriple().isX86()) {
+ if (S.X86().checkTargetClonesAttr(Strings, Locations, Buffer))
+ return;
+ }
+ Strings.clear();
+ for (auto &SmallStr : Buffer)
+ Strings.push_back(SmallStr.str());
- cast<FunctionDecl>(D)->setIsMultiVersion();
TargetClonesAttr *NewAttr = ::new (S.Context)
TargetClonesAttr(S.Context, AL, Strings.data(), Strings.size());
D->addAttr(NewAttr);
diff --git a/clang/lib/Sema/SemaRISCV.cpp b/clang/lib/Sema/SemaRISCV.cpp
index cc110e1115ed5..bc9d8330edf6c 100644
--- a/clang/lib/Sema/SemaRISCV.cpp
+++ b/clang/lib/Sema/SemaRISCV.cpp
@@ -1635,6 +1635,118 @@ bool SemaRISCV::isValidFMVExtension(StringRef Ext) {
return -1 != RISCVISAInfo::getRISCVFeaturesBitsInfo(Ext).second;
}
+enum FirstParam { Unsupported, Duplicate, Unknown };
+enum SecondParam { None, CPU, Tune };
+enum ThirdParam { Target, TargetClones, TargetVersion };
+
+bool SemaRISCV::checkTargetVersionAttr(StringRef Str, SourceLocation Loc) {
+ llvm::SmallVector<StringRef, 8> AttrStrs;
+ Str.split(AttrStrs, ';');
+
+ bool HasArch = false;
+ bool HasPriority = false;
+ bool HasDefault = false;
+ bool DuplicateAttr = false;
+ for (StringRef AttrStr : AttrStrs) {
+ // Only support arch=+ext,... syntax.
+ if (AttrStr.starts_with("arch=+")) {
+ if (HasArch)
+ DuplicateAttr = true;
+ HasArch = true;
+ ParsedTargetAttr TargetAttr =
+ getASTContext().getTargetInfo().parseTargetAttr(AttrStr);
+
+ if (TargetAttr.Features.empty() ||
+ llvm::any_of(TargetAttr.Features, [&](const StringRef Ext) {
+ return !isValidFMVExtension(Ext);
+ }))
+ return Diag(Loc, diag::warn_unsupported_target_attribute)
+ << Unsupported << None << AttrStr << TargetVersion;
+ } else if (AttrStr.starts_with("default")) {
+ if (HasDefault)
+ DuplicateAttr = true;
+ HasDefault = true;
+ } else if (AttrStr.consume_front("priority=")) {
+ if (HasPriority)
+ DuplicateAttr = true;
+ HasPriority = true;
+ unsigned Digit;
+ if (AttrStr.getAsInteger(0, Digit))
+ return Diag(Loc, diag::warn_unsupported_target_attribute)
+ << Unsupported << None << AttrStr << TargetVersion;
+ } else {
+ return Diag(Loc, diag::warn_unsupported_target_attribute)
+ << Unsupported << None << AttrStr << TargetVersion;
+ }
+ }
+
+ if (((HasPriority || HasArch) && HasDefault) || DuplicateAttr ||
+ (HasPriority && !HasArch))
+ return Diag(Loc, diag::warn_unsupported_target_attribute)
+ << Unsupported << None << Str << TargetVersion;
+
+ return false;
+}
+
+bool SemaRISCV::checkTargetClonesAttr(
+ SmallVectorImpl<StringRef> &Strs, SmallVectorImpl<SourceLocation> &Locs,
+ SmallVectorImpl<SmallString<64>> &Buffer) {
+ assert(Strs.size() == Locs.size() &&
+ "Mismatch between number of strings and locations");
+
+ bool HasDefault = false;
+ for (unsigned I = 0; I < Strs.size(); ++I) {
+ StringRef Str = Strs[I].trim();
+ SourceLocation Loc = Locs[I];
+
+ llvm::SmallVector<StringRef, 8> AttrStrs;
+ Str.split(AttrStrs, ";");
+
+ bool IsPriority = false;
+ bool IsDefault = false;
+ for (StringRef AttrStr : AttrStrs) {
+ // Only support arch=+ext,... syntax.
+ if (AttrStr.starts_with("arch=+")) {
+ ParsedTargetAttr TargetAttr =
+ getASTContext().getTargetInfo().parseTargetAttr(AttrStr);
+
+ if (TargetAttr.Features.empty() ||
+ llvm::any_of(TargetAttr.Features, [&](const StringRef Ext) {
+ return !isValidFMVExtension(Ext);
+ }))
+ return Diag(Loc, diag::warn_unsupported_target_attribute)
+ << Unsupported << None << Str << TargetClones;
+ } else if (AttrStr.starts_with("default")) {
+ if (HasDefault)
+ Diag(Loc, diag::warn_target_clone_duplicate_options);
+ IsDefault = true;
+ HasDefault = true;
+ } else if (AttrStr.consume_front("priority=")) {
+ IsPriority = true;
+ unsigned Digit;
+ if (AttrStr.getAsInteger(0, Digit))
+ return Diag(Loc, diag::warn_unsupported_target_attribute)
+ << Unsupported << None << Str << TargetClones;
+ } else {
+ return Diag(Loc, diag::warn_unsupported_target_attribute)
+ << Unsupported << None << Str << TargetClones;
+ }
+ }
+
+ if (IsPriority && IsDefault)
+ return Diag(Loc, diag::warn_unsupported_target_attribute)
+ << Unsupported << None << Str << TargetClones;
+
+ if (llvm::is_contained(Buffer, Str))
+ Diag(Loc, diag::warn_target_clone_duplicate_options);
+ Buffer.push_back(Str);
+ }
+ if (!HasDefault)
+ return Diag(Locs[0], diag::err_target_clone_must_have_default);
+
+ return false;
+}
+
SemaRISCV::SemaRISCV(Sema &S) : SemaBase(S) {}
} // namespace clang
diff --git a/clang/lib/Sema/SemaX86.cpp b/clang/lib/Sema/SemaX86.cpp
index 5c149bdec7073..ca76019eefc54 100644
--- a/clang/lib/Sema/SemaX86.cpp
+++ b/clang/lib/Sema/SemaX86.cpp
@@ -1056,4 +1056,62 @@ void SemaX86::handleForceAlignArgPointerAttr(Decl *D, const ParsedAttr &AL) {
X86ForceAlignArgPointerAttr(getASTContext(), AL));
}
+enum FirstParam { Unsupported, Duplicate, Unknown };
+enum SecondParam { None, CPU, Tune };
+enum ThirdParam { Target, TargetClones, TargetVersion };
+
+bool SemaX86::checkTargetClonesAttr(SmallVectorImpl<StringRef> &Strs,
+ SmallVectorImpl<SourceLocation> &Locs,
+ SmallVectorImpl<SmallString<64>> &Buffer) {
+ assert(Strs.size() == Locs.size() &&
+ "Mismatch between number of strings and locations");
+
+ bool HasDefault = false;
+ bool HasComma = false;
+ for (unsigned I = 0; I < Strs.size(); ++I) {
+ StringRef Str = Strs[I].trim();
+ SourceLocation Loc = Locs[I];
+
+ if (Str.empty() || Str.ends_with(','))
+ return Diag(Loc, diag::warn_unsupported_target_attribute)
+ << Unsupported << None << "" << TargetClones;
+
+ if (Str.contains(','))
+ HasComma = true;
+
+ StringRef LHS;
+ StringRef RHS = Str;
+ do {
+ std::tie(LHS, RHS) = RHS.split(',');
+ LHS = LHS.trim();
+ SourceLocation CurLoc = Loc.getLocWithOffset(LHS.data() - Str.data());
+
+ if (LHS.starts_with("arch=")) {
+ if (!getASTContext().getTargetInfo().isValidCPUName(
+ LHS.drop_front(sizeof("arch=") - 1)))
+ return Diag(CurLoc, diag::warn_unsupported_target_attribute)
+ << Unsupported << CPU << LHS.drop_front(sizeof("arch=") - 1)
+ << TargetClones;
+ } else if (LHS == "default")
+ HasDefault = true;
+ else if (!getASTContext().getTargetInfo().isValidFeatureName(LHS) ||
+ getASTContext().getTargetInfo().getFMVPriority(LHS) == 0)
+ return Diag(CurLoc, diag::warn_unsupported_target_attribute)
+ << Unsupported << None << LHS << TargetClones;
+
+ if (llvm::is_contained(Buffer, LHS))
+ Diag(CurLoc, diag::warn_target_clone_duplicate_options);
+ // Note: Add even if there are duplicates, since it changes name mangling.
+ Buffer.push_back(LHS);
+ } while (!RHS.empty());
+ }
+ if (HasComma && Strs.size() > 1)
+ Diag(Locs[0], diag::warn_target_clone_mixed_values);
+
+ if (!HasDefault)
+ return Diag(Locs[0], diag::err_target_clone_must_have_default);
+
+ return false;
+}
+
} // namespace clang
>From 889e7b8ccb58c1a649011b23e488f538ed716dcc Mon Sep 17 00:00:00 2001
From: Alexandros Lamprineas <alexandros.lamprineas at arm.com>
Date: Wed, 16 Jul 2025 16:21:42 +0100
Subject: [PATCH 2/2] changes from last revision:
For the SemaRISCV
* trim substrings before comparison
* refactor some conditional code
* check string equality for default version instead of starts_with
* remove redundant diagnostic and add a corresponding test
---
clang/lib/Sema/SemaRISCV.cpp | 17 +++++++----------
clang/test/SemaCXX/attr-target-clones-riscv.cpp | 3 +++
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/clang/lib/Sema/SemaRISCV.cpp b/clang/lib/Sema/SemaRISCV.cpp
index bc9d8330edf6c..28bc519197b77 100644
--- a/clang/lib/Sema/SemaRISCV.cpp
+++ b/clang/lib/Sema/SemaRISCV.cpp
@@ -1648,10 +1648,10 @@ bool SemaRISCV::checkTargetVersionAttr(StringRef Str, SourceLocation Loc) {
bool HasDefault = false;
bool DuplicateAttr = false;
for (StringRef AttrStr : AttrStrs) {
+ AttrStr = AttrStr.trim();
// Only support arch=+ext,... syntax.
if (AttrStr.starts_with("arch=+")) {
- if (HasArch)
- DuplicateAttr = true;
+ DuplicateAttr = HasArch;
HasArch = true;
ParsedTargetAttr TargetAttr =
getASTContext().getTargetInfo().parseTargetAttr(AttrStr);
@@ -1662,13 +1662,11 @@ bool SemaRISCV::checkTargetVersionAttr(StringRef Str, SourceLocation Loc) {
}))
return Diag(Loc, diag::warn_unsupported_target_attribute)
<< Unsupported << None << AttrStr << TargetVersion;
- } else if (AttrStr.starts_with("default")) {
- if (HasDefault)
- DuplicateAttr = true;
+ } else if (AttrStr == "default") {
+ DuplicateAttr = HasDefault;
HasDefault = true;
} else if (AttrStr.consume_front("priority=")) {
- if (HasPriority)
- DuplicateAttr = true;
+ DuplicateAttr = HasPriority;
HasPriority = true;
unsigned Digit;
if (AttrStr.getAsInteger(0, Digit))
@@ -1705,6 +1703,7 @@ bool SemaRISCV::checkTargetClonesAttr(
bool IsPriority = false;
bool IsDefault = false;
for (StringRef AttrStr : AttrStrs) {
+ AttrStr = AttrStr.trim();
// Only support arch=+ext,... syntax.
if (AttrStr.starts_with("arch=+")) {
ParsedTargetAttr TargetAttr =
@@ -1716,9 +1715,7 @@ bool SemaRISCV::checkTargetClonesAttr(
}))
return Diag(Loc, diag::warn_unsupported_target_attribute)
<< Unsupported << None << Str << TargetClones;
- } else if (AttrStr.starts_with("default")) {
- if (HasDefault)
- Diag(Loc, diag::warn_target_clone_duplicate_options);
+ } else if (AttrStr == "default") {
IsDefault = true;
HasDefault = true;
} else if (AttrStr.consume_front("priority=")) {
diff --git a/clang/test/SemaCXX/attr-target-clones-riscv.cpp b/clang/test/SemaCXX/attr-target-clones-riscv.cpp
index 102bb4b9b3d2b..7648284f80c48 100644
--- a/clang/test/SemaCXX/attr-target-clones-riscv.cpp
+++ b/clang/test/SemaCXX/attr-target-clones-riscv.cpp
@@ -9,6 +9,9 @@ void __attribute__((target_clones("default", "mtune=sifive-u74"))) mtune() {}
// expected-warning at +1 {{version list contains duplicate entries}}
void __attribute__((target_clones("default", "arch=+c", "arch=+c"))) dupVersion() {}
+// expected-warning at +1 {{version list contains duplicate entries}}
+void __attribute__((target_clones(" default", "default "))) dupDefault() {}
+
// expected-warning at +1 {{unsupported '' in the 'target_clones' attribute string; 'target_clones' attribute ignored}}
void __attribute__((target_clones("default", ""))) emptyVersion() {}
More information about the cfe-commits
mailing list