Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -2104,16 +2104,16 @@ Architecture == Other.Architecture && Features == Other.Features; } }; - ParsedTargetAttr parse() const { - return parse(getFeaturesStr()); + ParsedTargetAttr parse(StringRef SplitChars) const { + return parse(getFeaturesStr(), SplitChars); } - StringRef getArchitecture() const { + StringRef getArchitecture(StringRef SplitChars) const { StringRef Features = getFeaturesStr(); if (Features == "default") return {}; SmallVector AttrFeatures; - Features.split(AttrFeatures, ","); + Features.split_on_one_of(AttrFeatures, SplitChars); for (auto &Feature : AttrFeatures) { Feature = Feature.trim(); @@ -2125,12 +2125,13 @@ // Gets the list of features as simple string-refs with no +/- or 'no-'. // Only adds the items to 'Out' that are additions. - void getAddedFeatures(llvm::SmallVectorImpl &Out) const { + void getAddedFeatures(StringRef SplitChars, + llvm::SmallVectorImpl &Out) const { StringRef Features = getFeaturesStr(); if (Features == "default") return; SmallVector AttrFeatures; - Features.split(AttrFeatures, ","); + Features.split_on_one_of(AttrFeatures, SplitChars); for (auto &Feature : AttrFeatures) { Feature = Feature.trim(); @@ -2142,19 +2143,19 @@ } template - ParsedTargetAttr parse(Compare cmp) const { - ParsedTargetAttr Attrs = parse(); + ParsedTargetAttr parse(Compare cmp, StringRef SplitChars) const { + ParsedTargetAttr Attrs = parse(SplitChars); llvm::sort(std::begin(Attrs.Features), std::end(Attrs.Features), cmp); return Attrs; } bool isDefaultVersion() const { return getFeaturesStr() == "default"; } - static ParsedTargetAttr parse(StringRef Features) { + static ParsedTargetAttr parse(StringRef Features, StringRef SplitChars) { ParsedTargetAttr Ret; if (Features == "default") return Ret; SmallVector AttrFeatures; - Features.split(AttrFeatures, ","); + Features.split_on_one_of(AttrFeatures, SplitChars); // Grab the various features and prepend a "+" to turn on the feature to // the backend and add them to our existing set of features. Index: clang/include/clang/Basic/TargetInfo.h =================================================================== --- clang/include/clang/Basic/TargetInfo.h +++ clang/include/clang/Basic/TargetInfo.h @@ -1076,6 +1076,13 @@ return true; } + /// Whether the '+' character as a feature separator is valid for the + /// Attribute-Target handling. AArch64 in GCC does, so we likely should as + /// well. + virtual StringRef getTargetAttrSplitChars() const { + return ","; + } + /// Use the specified ABI. /// /// \return False on error (invalid ABI name). Index: clang/lib/Basic/Targets/AArch64.h =================================================================== --- clang/lib/Basic/Targets/AArch64.h +++ clang/lib/Basic/Targets/AArch64.h @@ -50,6 +50,7 @@ bool setABI(const std::string &Name) override; bool isValidCPUName(StringRef Name) const override; + StringRef getTargetAttrSplitChars() const override; void fillValidCPUList(SmallVectorImpl &Values) const override; bool setCPU(const std::string &Name) override; Index: clang/lib/Basic/Targets/AArch64.cpp =================================================================== --- clang/lib/Basic/Targets/AArch64.cpp +++ clang/lib/Basic/Targets/AArch64.cpp @@ -105,7 +105,11 @@ bool AArch64TargetInfo::isValidCPUName(StringRef Name) const { return Name == "generic" || - llvm::AArch64::parseCPUArch(Name) != llvm::AArch64::ArchKind::INVALID; + llvm::AArch64::parseArch(Name) != llvm::AArch64::ArchKind::INVALID; +} + +StringRef AArch64TargetInfo::getTargetAttrSplitChars() const { + return ",+"; } bool AArch64TargetInfo::setCPU(const std::string &Name) { Index: clang/lib/CodeGen/CodeGenModule.cpp =================================================================== --- clang/lib/CodeGen/CodeGenModule.cpp +++ clang/lib/CodeGen/CodeGenModule.cpp @@ -959,15 +959,16 @@ Out << '.'; const TargetInfo &Target = CGM.getTarget(); - TargetAttr::ParsedTargetAttr Info = - Attr->parse([&Target](StringRef LHS, StringRef RHS) { + TargetAttr::ParsedTargetAttr Info = Attr->parse( + [&Target](StringRef LHS, StringRef RHS) { // Multiversioning doesn't allow "no-${feature}", so we can // only have "+" prefixes here. assert(LHS.startswith("+") && RHS.startswith("+") && "Features should always have a prefix."); return Target.multiVersionSortPriority(LHS.substr(1)) > Target.multiVersionSortPriority(RHS.substr(1)); - }); + }, + Target.getTargetAttrSplitChars()); bool IsFirst = true; @@ -1680,7 +1681,8 @@ // get and parse the target attribute so we can get the cpu for // the function. if (TD) { - TargetAttr::ParsedTargetAttr ParsedAttr = TD->parse(); + TargetAttr::ParsedTargetAttr ParsedAttr = + TD->parse(getTarget().getTargetAttrSplitChars()); if (ParsedAttr.Architecture != "" && getTarget().isValidCPUName(ParsedAttr.Architecture)) TargetCPU = ParsedAttr.Architecture; @@ -2827,10 +2829,10 @@ const auto *TA = CurFD->getAttr(); llvm::SmallVector Feats; - TA->getAddedFeatures(Feats); + TA->getAddedFeatures(getTarget().getTargetAttrSplitChars(), Feats); Options.emplace_back(cast(Func), - TA->getArchitecture(), Feats); + TA->getArchitecture(getTarget().getTargetAttrSplitChars()), Feats); }); llvm::Function *ResolverFunc; @@ -5803,7 +5805,8 @@ TargetAttr::ParsedTargetAttr CodeGenModule::filterFunctionTargetAttrs(const TargetAttr *TD) { assert(TD != nullptr); - TargetAttr::ParsedTargetAttr ParsedAttr = TD->parse(); + TargetAttr::ParsedTargetAttr ParsedAttr = + TD->parse(getTarget().getTargetAttrSplitChars()); ParsedAttr.Features.erase( llvm::remove_if(ParsedAttr.Features, Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -9625,8 +9625,9 @@ static bool CheckMultiVersionValue(Sema &S, const FunctionDecl *FD) { const auto *TA = FD->getAttr(); assert(TA && "MultiVersion Candidate requires a target attribute"); - TargetAttr::ParsedTargetAttr ParseInfo = TA->parse(); const TargetInfo &TargetInfo = S.Context.getTargetInfo(); + TargetAttr::ParsedTargetAttr ParseInfo = + TA->parse(TargetInfo.getTargetAttrSplitChars()); enum ErrType { Feature = 0, Architecture = 1 }; if (!ParseInfo.Architecture.empty() && @@ -9875,7 +9876,8 @@ bool &Redeclaration, NamedDecl *&OldDecl, bool &MergeTypeWithPrevious, LookupResult &Previous) { const auto *OldTA = OldFD->getAttr(); - TargetAttr::ParsedTargetAttr NewParsed = NewTA->parse(); + TargetAttr::ParsedTargetAttr NewParsed = + NewTA->parse(S.getASTContext().getTargetInfo().getTargetAttrSplitChars()); // Sort order doesn't matter, it just needs to be consistent. llvm::sort(NewParsed.Features); @@ -9920,7 +9922,8 @@ } TargetAttr::ParsedTargetAttr OldParsed = - OldTA->parse(std::less()); + OldTA->parse(std::less(), + S.getASTContext().getTargetInfo().getTargetAttrSplitChars()); if (OldParsed == NewParsed) { S.Diag(NewFD->getLocation(), diag::err_multiversion_duplicate); @@ -9975,7 +9978,8 @@ TargetAttr::ParsedTargetAttr NewParsed; if (NewTA) { - NewParsed = NewTA->parse(); + NewParsed = NewTA->parse( + S.getASTContext().getTargetInfo().getTargetAttrSplitChars()); llvm::sort(NewParsed.Features); } @@ -10000,8 +10004,9 @@ return false; } - TargetAttr::ParsedTargetAttr CurParsed = - CurTA->parse(std::less()); + TargetAttr::ParsedTargetAttr CurParsed = CurTA->parse( + std::less(), + S.getASTContext().getTargetInfo().getTargetAttrSplitChars()); if (CurParsed == NewParsed) { S.Diag(NewFD->getLocation(), diag::err_multiversion_duplicate); S.Diag(CurFD->getLocation(), diag::note_previous_declaration); Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -2972,7 +2972,8 @@ return Diag(LiteralLoc, diag::warn_unsupported_target_attribute) << Unsupported << None << Str; - TargetAttr::ParsedTargetAttr ParsedAttrs = TargetAttr::parse(AttrStr); + TargetAttr::ParsedTargetAttr ParsedAttrs = TargetAttr::parse( + AttrStr, Context.getTargetInfo().getTargetAttrSplitChars()); if (!ParsedAttrs.Architecture.empty() && !Context.getTargetInfo().isValidCPUName(ParsedAttrs.Architecture)) Index: clang/test/CodeGen/aarch64-target-attr.c =================================================================== --- /dev/null +++ clang/test/CodeGen/aarch64-target-attr.c @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -triple aarch64-linux-gnu -emit-llvm -o - %s | FileCheck %s +__attribute__((target("arch=armv8-a"))) +void f1(void){} +//CHECK: define void @f1() [[F1_ATTRS:#[0-9]+]] + +__attribute__((target("arch=armv8-a+lse"))) +void f2(void){} +//CHECK: define void @f2() [[F2_ATTRS:#[0-9]+]] + +__attribute__((target("arch=armv8.4-a"))) +void f3(void){} +//CHECK: define void @f3() [[F3_ATTRS:#[0-9]+]] + +__attribute__((target("lse"))) +void f4(void){} +//CHECK: define void @f4() [[F4_ATTRS:#[0-9]+]] + +// CHECK: [[F1_ATTRS]] = { {{.*}} "target-cpu"="armv8-a" +// CHECK: [[F2_ATTRS]] = { {{.*}} "target-cpu"="armv8-a"{{.*}} "target-features"="+lse" +// CHECK: [[F3_ATTRS]] = { {{.*}} "target-cpu"="armv8.4-a" +// CHECK: [[F4_ATTRS]] = { {{.*}} "target-features"="+lse" Index: llvm/include/llvm/ADT/StringRef.h =================================================================== --- llvm/include/llvm/ADT/StringRef.h +++ llvm/include/llvm/ADT/StringRef.h @@ -765,6 +765,24 @@ StringRef Separator, int MaxSplit = -1, bool KeepEmpty = true) const; + /// Split into substrings around the occurrences of a set of separators. + /// + /// Each substring is stored in \p A. If \p MaxSplit is >= 0, at most + /// \p MaxSplit splits are done and consequently <= \p MaxSplit + 1 + /// elements are added to A. + /// If \p KeepEmpty is false, empty strings are not added to \p A. They + /// still count when considering \p MaxSplit + /// An useful invariant is that + /// Separator.join(A) == *this if MaxSplit == -1 and KeepEmpty == true + /// + /// \param A - Where to put the substrings. + /// \param Separators - The characters to split on. + /// \param MaxSplit - The maximum number of times the string is split. + /// \param KeepEmpty - True if empty substring should be added. + void split_on_one_of(SmallVectorImpl &A, + StringRef Separators, int MaxSplit = -1, + bool KeepEmpty = true) const; + /// Split into substrings around the occurrences of a separator character. /// /// Each substring is stored in \p A. If \p MaxSplit is >= 0, at most Index: llvm/lib/Support/StringRef.cpp =================================================================== --- llvm/lib/Support/StringRef.cpp +++ llvm/lib/Support/StringRef.cpp @@ -337,6 +337,33 @@ A.push_back(S); } +void StringRef::split_on_one_of(SmallVectorImpl &A, + StringRef Separators, int MaxSplit, + bool KeepEmpty) const { + StringRef S = *this; + + // Count down from MaxSplit. When MaxSplit is -1, this will just split + // "forever". This doesn't support splitting more than 2^31 times + // intentionally; if we ever want that we can make MaxSplit a 64-bit integer + // but that seems unlikely to be useful. + while (MaxSplit-- != 0) { + size_t Idx = S.find_first_of(Separators); + if (Idx == npos) + break; + + // Push this split. + if (KeepEmpty || Idx > 0) + A.push_back(S.slice(0, Idx)); + + // Jump forward. + S = S.slice(Idx + 1, npos); + } + + // Push the tail. + if (KeepEmpty || !S.empty()) + A.push_back(S); +} + void StringRef::split(SmallVectorImpl &A, char Separator, int MaxSplit, bool KeepEmpty) const { StringRef S = *this;