Author: Alexandros Lamprineas
Date: 2025-12-09T13:31:10Z
New Revision: 1b82c16fa8e017ff4de4f96c31af682e5a6ea649

URL: 
https://github.com/llvm/llvm-project/commit/1b82c16fa8e017ff4de4f96c31af682e5a6ea649
DIFF: 
https://github.com/llvm/llvm-project/commit/1b82c16fa8e017ff4de4f96c31af682e5a6ea649.diff

LOG: [FMV][AArch64] Allow user to override version priority. (#150267)

Implements https://github.com/ARM-software/acle/pull/404

This allows the user to specify "featA+featB;priority=[1-255]" where
priority=255 means highest priority. If the explicit priority string is
omitted then the priority of "featA+featB" is implied, which is lower
than priority=1.

Internally this gets expanded using special FMV features P0 ... P7 which
can encode up to 256-1 priority levels (excluding all zeros). Those do
not have corresponding detection bit at pos FEAT_#enum so I made this
field optional in FMVInfo. Also they don't affect the codegen or name
mangling of versioned functions.

Added: 
    clang/test/CodeGen/AArch64/fmv-explicit-priority.c

Modified: 
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Sema/SemaARM.h
    clang/include/clang/Sema/SemaRISCV.h
    clang/lib/CodeGen/Targets/AArch64.cpp
    clang/lib/Sema/SemaARM.cpp
    clang/lib/Sema/SemaDeclAttr.cpp
    clang/lib/Sema/SemaRISCV.cpp
    clang/test/AST/attr-target-version.c
    clang/test/CodeGen/AArch64/fmv-duplicate-mangled-name.c
    clang/test/Sema/attr-target-clones-aarch64.c
    clang/test/Sema/attr-target-version.c
    llvm/include/llvm/Analysis/TargetTransformInfo.h
    llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
    llvm/include/llvm/TargetParser/AArch64FeatPriorities.inc
    llvm/include/llvm/TargetParser/AArch64TargetParser.h
    llvm/lib/Analysis/TargetTransformInfo.cpp
    llvm/lib/Target/AArch64/AArch64FMV.td
    llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
    llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h
    llvm/lib/TargetParser/AArch64TargetParser.cpp
    llvm/lib/Transforms/IPO/GlobalOpt.cpp
    llvm/test/Transforms/GlobalOpt/resolve-fmv-ifunc.ll
    llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 9147b6a041c70..fec278c21a89e 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12945,6 +12945,12 @@ def warn_target_clone_duplicate_options
 def warn_target_clone_no_impact_options
     : Warning<"version list contains entries that don't impact code 
generation">,
       InGroup<FunctionMultiVersioning>;
+def warn_version_priority_out_of_range
+    : Warning<"version priority '%0' is outside the allowed range [1-255]; 
ignoring priority">,
+      InGroup<FunctionMultiVersioning>;
+def warn_invalid_default_version_priority
+    : Warning<"priority of default version cannot be overridden; ignoring 
priority">,
+      InGroup<FunctionMultiVersioning>;
 
 // three-way comparison operator diagnostics
 def err_implied_comparison_category_type_not_found : Error<

diff  --git a/clang/include/clang/Sema/SemaARM.h 
b/clang/include/clang/Sema/SemaARM.h
index 1e8941a381dc5..af8e0e9047171 100644
--- a/clang/include/clang/Sema/SemaARM.h
+++ b/clang/include/clang/Sema/SemaARM.h
@@ -92,7 +92,8 @@ class SemaARM : public SemaBase {
   /// false otherwise.
   bool areLaxCompatibleSveTypes(QualType FirstType, QualType SecondType);
 
-  bool checkTargetVersionAttr(const StringRef Str, const SourceLocation Loc);
+  bool checkTargetVersionAttr(const StringRef Param, const SourceLocation Loc,
+                              SmallString<64> &NewParam);
   bool checkTargetClonesAttr(SmallVectorImpl<StringRef> &Params,
                              SmallVectorImpl<SourceLocation> &Locs,
                              SmallVectorImpl<SmallString<64>> &NewParams);

diff  --git a/clang/include/clang/Sema/SemaRISCV.h 
b/clang/include/clang/Sema/SemaRISCV.h
index 844cc3ce4a440..863b8a143f48a 100644
--- a/clang/include/clang/Sema/SemaRISCV.h
+++ b/clang/include/clang/Sema/SemaRISCV.h
@@ -56,7 +56,8 @@ class SemaRISCV : public SemaBase {
 
   std::unique_ptr<sema::RISCVIntrinsicManager> IntrinsicManager;
 
-  bool checkTargetVersionAttr(const StringRef Param, const SourceLocation Loc);
+  bool checkTargetVersionAttr(const StringRef Param, const SourceLocation Loc,
+                              SmallString<64> &NewParam);
   bool checkTargetClonesAttr(SmallVectorImpl<StringRef> &Params,
                              SmallVectorImpl<SourceLocation> &Locs,
                              SmallVectorImpl<SmallString<64>> &NewParams);

diff  --git a/clang/lib/CodeGen/Targets/AArch64.cpp 
b/clang/lib/CodeGen/Targets/AArch64.cpp
index 74bf5531c942a..77b947e81f5fc 100644
--- a/clang/lib/CodeGen/Targets/AArch64.cpp
+++ b/clang/lib/CodeGen/Targets/AArch64.cpp
@@ -1359,9 +1359,10 @@ void AArch64ABIInfo::appendAttributeMangling(StringRef 
AttrStr,
 
   llvm::SmallDenseSet<StringRef, 8> UniqueFeats;
   for (auto &Feat : Features)
-    if (auto Ext = llvm::AArch64::parseFMVExtension(Feat))
-      if (UniqueFeats.insert(Ext->Name).second)
-        Out << 'M' << Ext->Name;
+    if (getTarget().doesFeatureAffectCodeGen(Feat))
+      if (auto Ext = llvm::AArch64::parseFMVExtension(Feat))
+        if (UniqueFeats.insert(Ext->Name).second)
+          Out << 'M' << Ext->Name;
 }
 
 std::unique_ptr<TargetCodeGenInfo>

diff  --git a/clang/lib/Sema/SemaARM.cpp b/clang/lib/Sema/SemaARM.cpp
index a5164a94b57fa..81504b74c5e45 100644
--- a/clang/lib/Sema/SemaARM.cpp
+++ b/clang/lib/Sema/SemaARM.cpp
@@ -1593,19 +1593,54 @@ bool SemaARM::areLaxCompatibleSveTypes(QualType 
FirstType,
          IsLaxCompatible(SecondType, FirstType);
 }
 
+static void appendFeature(StringRef Feat, SmallString<64> &Buffer) {
+  if (!Buffer.empty())
+    Buffer.append("+");
+  Buffer.append(Feat);
+}
+
+static void convertPriorityString(unsigned Priority,
+                                  SmallString<64> &NewParam) {
+  StringRef PriorityString[8] = {"P0", "P1", "P2", "P3",
+                                 "P4", "P5", "P6", "P7"};
+
+  assert(Priority > 0 && Priority < 256 && "priority out of range");
+  // Convert priority=[1-255] -> P0 + ... + P7
+  for (unsigned BitPos = 0; BitPos < 8; ++BitPos)
+    if (Priority & (1U << BitPos))
+      appendFeature(PriorityString[BitPos], NewParam);
+}
+
 bool SemaARM::checkTargetVersionAttr(const StringRef Param,
-                                     const SourceLocation Loc) {
+                                     const SourceLocation Loc,
+                                     SmallString<64> &NewParam) {
   using namespace DiagAttrParams;
 
+  auto [LHS, RHS] = Param.split(';');
+  RHS = RHS.trim();
+  bool IsDefault = false;
   llvm::SmallVector<StringRef, 8> Features;
-  Param.split(Features, '+');
+  LHS.split(Features, '+');
   for (StringRef Feat : Features) {
     Feat = Feat.trim();
     if (Feat == "default")
-      continue;
-    if (!getASTContext().getTargetInfo().validateCpuSupports(Feat))
+      IsDefault = true;
+    else if (!getASTContext().getTargetInfo().validateCpuSupports(Feat))
       return Diag(Loc, diag::warn_unsupported_target_attribute)
              << Unsupported << None << Feat << TargetVersion;
+    appendFeature(Feat, NewParam);
+  }
+
+  if (!RHS.empty() && RHS.consume_front("priority=")) {
+    if (IsDefault)
+      Diag(Loc, diag::warn_invalid_default_version_priority);
+    else {
+      unsigned Digit;
+      if (RHS.getAsInteger(0, Digit) || Digit < 1 || Digit > 255)
+        Diag(Loc, diag::warn_version_priority_out_of_range) << RHS;
+      else
+        convertPriorityString(Digit, NewParam);
+    }
   }
   return false;
 }
@@ -1627,15 +1662,21 @@ bool SemaARM::checkTargetClonesAttr(
     const StringRef Param = Params[I].trim();
     const SourceLocation &Loc = Locs[I];
 
-    if (Param.empty())
+    auto [LHS, RHS] = Param.split(';');
+    RHS = RHS.trim();
+    bool HasPriority = !RHS.empty() && RHS.consume_front("priority=");
+
+    if (LHS.empty())
       return Diag(Loc, diag::warn_unsupported_target_attribute)
              << Unsupported << None << "" << TargetClones;
 
-    if (Param == "default") {
+    if (LHS == "default") {
       if (HasDefault)
         Diag(Loc, diag::warn_target_clone_duplicate_options);
       else {
-        NewParams.push_back(Param);
+        if (HasPriority)
+          Diag(Loc, diag::warn_invalid_default_version_priority);
+        NewParams.push_back(LHS);
         HasDefault = true;
       }
       continue;
@@ -1644,7 +1685,7 @@ bool SemaARM::checkTargetClonesAttr(
     bool HasCodeGenImpact = false;
     llvm::SmallVector<StringRef, 8> Features;
     llvm::SmallVector<StringRef, 8> ValidFeatures;
-    Param.split(Features, '+');
+    LHS.split(Features, '+');
     for (StringRef Feat : Features) {
       Feat = Feat.trim();
       if (!getASTContext().getTargetInfo().validateCpuSupports(Feat)) {
@@ -1674,6 +1715,14 @@ bool SemaARM::checkTargetClonesAttr(
       continue;
     }
 
+    if (HasPriority) {
+      unsigned Digit;
+      if (RHS.getAsInteger(0, Digit) || Digit < 1 || Digit > 255)
+        Diag(Loc, diag::warn_version_priority_out_of_range) << RHS;
+      else
+        convertPriorityString(Digit, NewParam);
+    }
+
     // Valid non-default argument.
     NewParams.push_back(NewParam);
     HasNonDefault = true;

diff  --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 81986264d5696..43f11e5595844 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -3445,19 +3445,20 @@ bool Sema::checkTargetAttr(SourceLocation LiteralLoc, 
StringRef AttrStr) {
 static void handleTargetVersionAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   StringRef Param;
   SourceLocation Loc;
+  SmallString<64> NewParam;
   if (!S.checkStringLiteralArgumentAttr(AL, 0, Param, &Loc))
     return;
 
   if (S.Context.getTargetInfo().getTriple().isAArch64()) {
-    if (S.ARM().checkTargetVersionAttr(Param, Loc))
+    if (S.ARM().checkTargetVersionAttr(Param, Loc, NewParam))
       return;
   } else if (S.Context.getTargetInfo().getTriple().isRISCV()) {
-    if (S.RISCV().checkTargetVersionAttr(Param, Loc))
+    if (S.RISCV().checkTargetVersionAttr(Param, Loc, NewParam))
       return;
   }
 
   TargetVersionAttr *NewAttr =
-      ::new (S.Context) TargetVersionAttr(S.Context, AL, Param);
+      ::new (S.Context) TargetVersionAttr(S.Context, AL, NewParam);
   D->addAttr(NewAttr);
 }
 

diff  --git a/clang/lib/Sema/SemaRISCV.cpp b/clang/lib/Sema/SemaRISCV.cpp
index 75dba8049a43c..cfe30fdc73ba1 100644
--- a/clang/lib/Sema/SemaRISCV.cpp
+++ b/clang/lib/Sema/SemaRISCV.cpp
@@ -1739,7 +1739,8 @@ bool SemaRISCV::isValidFMVExtension(StringRef Ext) {
 }
 
 bool SemaRISCV::checkTargetVersionAttr(const StringRef Param,
-                                       const SourceLocation Loc) {
+                                       const SourceLocation Loc,
+                                       SmallString<64> &NewParam) {
   using namespace DiagAttrParams;
 
   llvm::SmallVector<StringRef, 8> AttrStrs;
@@ -1785,6 +1786,7 @@ bool SemaRISCV::checkTargetVersionAttr(const StringRef 
Param,
     return Diag(Loc, diag::warn_unsupported_target_attribute)
            << Unsupported << None << Param << TargetVersion;
 
+  NewParam = Param;
   return false;
 }
 

diff  --git a/clang/test/AST/attr-target-version.c 
b/clang/test/AST/attr-target-version.c
index b537f5e685a31..c216cd6d1a28a 100644
--- a/clang/test/AST/attr-target-version.c
+++ b/clang/test/AST/attr-target-version.c
@@ -2,7 +2,29 @@
 
 int __attribute__((target_version("sve2-bitperm + sha2"))) foov(void) { return 
1; }
 int __attribute__((target_clones(" lse + fp + sha3 ", "default"))) fooc(void) 
{ return 2; }
-// CHECK: TargetVersionAttr
-// CHECK: sve2-bitperm + sha2
-// CHECK: TargetClonesAttr
-// CHECK: fp+lse+sha3 default
+
+int __attribute__((target_version("aes;priority=1"))) explicit_priority(void) 
{ return 1; }
+int __attribute__((target_version("bf16;priority=2"))) explicit_priority(void) 
{ return 2; }
+int __attribute__((target_version("crc;priority=4"))) explicit_priority(void) 
{ return 4; }
+int __attribute__((target_version("dpb2;priority=8"))) explicit_priority(void) 
{ return 8; }
+int __attribute__((target_version("fp16fml;priority=16"))) 
explicit_priority(void) { return 16; }
+int __attribute__((target_version("dotprod;priority=32"))) 
explicit_priority(void) { return 32; }
+int __attribute__((target_version("sve;priority=64"))) explicit_priority(void) 
{ return 64; }
+int __attribute__((target_version("mops;priority=128"))) 
explicit_priority(void) { return 128; }
+
+int __attribute__((target_clones("simd;priority=255", "default"))) 
explicit_priority(void) {
+  return 0;
+}
+
+// CHECK: TargetVersionAttr {{.*}} "sve2-bitperm+sha2"
+// CHECK: TargetClonesAttr {{.*}} fp+lse+sha3 default
+
+// CHECK: TargetVersionAttr {{.*}} "aes+P0"
+// CHECK: TargetVersionAttr {{.*}} "bf16+P1"
+// CHECK: TargetVersionAttr {{.*}} "crc+P2"
+// CHECK: TargetVersionAttr {{.*}} "dpb2+P3"
+// CHECK: TargetVersionAttr {{.*}} "fp16fml+P4"
+// CHECK: TargetVersionAttr {{.*}} "dotprod+P5"
+// CHECK: TargetVersionAttr {{.*}} "sve+P6"
+// CHECK: TargetVersionAttr {{.*}} "mops+P7"
+// CHECK: TargetClonesAttr {{.*}} simd+P0+P1+P2+P3+P4+P5+P6+P7 default

diff  --git a/clang/test/CodeGen/AArch64/fmv-duplicate-mangled-name.c 
b/clang/test/CodeGen/AArch64/fmv-duplicate-mangled-name.c
index e7e611e09542e..ebe5b75cf7946 100644
--- a/clang/test/CodeGen/AArch64/fmv-duplicate-mangled-name.c
+++ b/clang/test/CodeGen/AArch64/fmv-duplicate-mangled-name.c
@@ -1,5 +1,7 @@
 // RUN: %clang_cc1 -triple aarch64-linux-gnu -verify -emit-llvm-only %s 
-DCHECK_IMPLICIT_DEFAULT
 // RUN: %clang_cc1 -triple aarch64-linux-gnu -verify -emit-llvm-only %s 
-DCHECK_EXPLICIT_DEFAULT
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -verify -emit-llvm-only %s 
-DCHECK_EXPLICIT_VERSION_PRIORITY
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -verify -emit-llvm-only %s 
-DCHECK_EXPLICIT_CLONES_PRIORITY
 
 #if defined(CHECK_IMPLICIT_DEFAULT)
 
@@ -21,4 +23,18 @@ __attribute__((target_version("default"))) int 
explicit_default_bad(void) { retu
 // expected-note@-2 {{previous definition is here}}
 __attribute__((target_clones("aes", "lse", "default"))) int 
explicit_default_bad(void) { return 1; }
 
+#elif defined(CHECK_EXPLICIT_VERSION_PRIORITY)
+
+__attribute__((target_version("aes"))) int explicit_version_priority(void) { 
return 0; }
+// expected-error@+2 {{definition with same mangled name 
'explicit_version_priority._Maes' as another definition}}
+// expected-note@-2 {{previous definition is here}}
+__attribute__((target_version("aes;priority=10"))) int 
explicit_version_priority(void) { return 1; }
+
+#elif defined(CHECK_EXPLICIT_CLONES_PRIORITY)
+
+__attribute__((target_version("aes;priority=20"))) int 
explicit_clones_priority(void) { return 0; }
+// expected-error@+2 {{definition with same mangled name 
'explicit_clones_priority._Maes' as another definition}}
+// expected-note@-2 {{previous definition is here}}
+__attribute__((target_clones("aes;priority=5", "lse"))) int 
explicit_clones_priority(void) { return 1; }
+
 #endif

diff  --git a/clang/test/CodeGen/AArch64/fmv-explicit-priority.c 
b/clang/test/CodeGen/AArch64/fmv-explicit-priority.c
new file mode 100644
index 0000000000000..1abf330ffee49
--- /dev/null
+++ b/clang/test/CodeGen/AArch64/fmv-explicit-priority.c
@@ -0,0 +1,204 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py 
UTC_ARGS: --function-signature --check-attributes --check-globals 
--include-generated-funcs
+// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -O3 -fno-inline -emit-llvm 
-o - %s | FileCheck %s
+
+__attribute__((target_version("lse;priority=30"))) int foo(void) { return 1; }
+__attribute__((target_version("sve2;priority=20"))) int foo(void) { return 2; }
+__attribute__((target_version("sve;priority=10"))) int foo(void) { return 3; }
+__attribute__((target_version("default"))) int foo(void) { return 0; }
+
+__attribute__((target_clones("lse+sve2;priority=3", "lse;priority=2", 
"sve;priority=1", "default")))
+int fmv_caller(void) { return foo(); }
+
+
+__attribute__((target_version("aes"))) int bar(void) { return 1; }
+__attribute__((target_version("sm4;priority=5"))) int bar(void) { return 2; }
+__attribute__((target_version("default"))) int bar(void) { return 0; }
+
+__attribute__((target("aes"))) int regular_caller_aes() { return bar(); }
+__attribute__((target("sm4"))) int regular_caller_sm4() { return bar(); }
+//.
+// CHECK: @__aarch64_cpu_features = external dso_local local_unnamed_addr 
global { i64 }
+// CHECK: @foo = weak_odr ifunc i32 (), ptr @foo.resolver
+// CHECK: @fmv_caller = weak_odr ifunc i32 (), ptr @fmv_caller.resolver
+// CHECK: @bar = weak_odr ifunc i32 (), ptr @bar.resolver
+//.
+// CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync 
nounwind willreturn memory(none)
+// CHECK-LABEL: define {{[^@]+}}@foo._Mlse
+// CHECK-SAME: () #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    ret i32 1
+//
+//
+// CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync 
nounwind willreturn memory(none) vscale_range(1,16)
+// CHECK-LABEL: define {{[^@]+}}@foo._Msve2
+// CHECK-SAME: () #[[ATTR1:[0-9]+]] {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    ret i32 2
+//
+//
+// CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync 
nounwind willreturn memory(none) vscale_range(1,16)
+// CHECK-LABEL: define {{[^@]+}}@foo._Msve
+// CHECK-SAME: () #[[ATTR2:[0-9]+]] {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    ret i32 3
+//
+//
+// CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync 
nounwind willreturn memory(none)
+// CHECK-LABEL: define {{[^@]+}}@foo.default
+// CHECK-SAME: () #[[ATTR3:[0-9]+]] {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    ret i32 0
+//
+//
+// CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync 
nounwind willreturn memory(none) vscale_range(1,16)
+// CHECK-LABEL: define {{[^@]+}}@fmv_caller._MlseMsve2
+// CHECK-SAME: () #[[ATTR4:[0-9]+]] {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[CALL:%.*]] = tail call i32 @foo._Mlse()
+// CHECK-NEXT:    ret i32 [[CALL]]
+//
+//
+// CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync 
nounwind willreturn memory(none) vscale_range(1,16)
+// CHECK-LABEL: define {{[^@]+}}@fmv_caller._Mlse
+// CHECK-SAME: () #[[ATTR5:[0-9]+]] {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[CALL:%.*]] = tail call i32 @foo._Mlse()
+// CHECK-NEXT:    ret i32 [[CALL]]
+//
+//
+// CHECK: Function Attrs: noinline nounwind vscale_range(1,16)
+// CHECK-LABEL: define {{[^@]+}}@fmv_caller._Msve
+// CHECK-SAME: () #[[ATTR6:[0-9]+]] {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[CALL:%.*]] = tail call i32 @foo() #[[ATTR13:[0-9]+]]
+// CHECK-NEXT:    ret i32 [[CALL]]
+//
+//
+// CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync 
nounwind willreturn memory(none) vscale_range(1,16)
+// CHECK-LABEL: define {{[^@]+}}@fmv_caller.default
+// CHECK-SAME: () #[[ATTR7:[0-9]+]] {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[CALL:%.*]] = tail call i32 @foo.default()
+// CHECK-NEXT:    ret i32 [[CALL]]
+//
+//
+// CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync 
nounwind willreturn memory(none)
+// CHECK-LABEL: define {{[^@]+}}@bar._Maes
+// CHECK-SAME: () #[[ATTR8:[0-9]+]] {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    ret i32 1
+//
+//
+// CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync 
nounwind willreturn memory(none)
+// CHECK-LABEL: define {{[^@]+}}@bar._Msm4
+// CHECK-SAME: () #[[ATTR9:[0-9]+]] {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    ret i32 2
+//
+//
+// CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync 
nounwind willreturn memory(none)
+// CHECK-LABEL: define {{[^@]+}}@bar.default
+// CHECK-SAME: () #[[ATTR3]] {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    ret i32 0
+//
+//
+// CHECK: Function Attrs: noinline nounwind
+// CHECK-LABEL: define {{[^@]+}}@regular_caller_aes
+// CHECK-SAME: () local_unnamed_addr #[[ATTR10:[0-9]+]] {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[CALL:%.*]] = tail call i32 @bar() #[[ATTR13]]
+// CHECK-NEXT:    ret i32 [[CALL]]
+//
+//
+// CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync 
nounwind willreturn memory(none)
+// CHECK-LABEL: define {{[^@]+}}@regular_caller_sm4
+// CHECK-SAME: () local_unnamed_addr #[[ATTR11:[0-9]+]] {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[CALL:%.*]] = tail call i32 @bar._Msm4()
+// CHECK-NEXT:    ret i32 [[CALL]]
+//
+//
+// CHECK: Function Attrs: disable_sanitizer_instrumentation
+// CHECK-LABEL: define {{[^@]+}}@foo.resolver
+// CHECK-SAME: () #[[ATTR12:[0-9]+]] comdat {
+// CHECK-NEXT:  resolver_entry:
+// CHECK-NEXT:    tail call void @__init_cpu_features_resolver()
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = and i64 [[TMP0]], 128
+// CHECK-NEXT:    [[DOTNOT:%.*]] = icmp eq i64 [[TMP1]], 0
+// CHECK-NEXT:    br i1 [[DOTNOT]], label [[RESOLVER_ELSE:%.*]], label 
[[COMMON_RET:%.*]]
+// CHECK:       common.ret:
+// CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi ptr [ @foo._Mlse, 
[[RESOLVER_ENTRY:%.*]] ], [ @foo._Msve2, [[RESOLVER_ELSE]] ], [ 
[[FOO__MSVE_FOO_DEFAULT:%.*]], [[RESOLVER_ELSE2:%.*]] ]
+// CHECK-NEXT:    ret ptr [[COMMON_RET_OP]]
+// CHECK:       resolver_else:
+// CHECK-NEXT:    [[TMP2:%.*]] = and i64 [[TMP0]], 69793284352
+// CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i64 [[TMP2]], 69793284352
+// CHECK-NEXT:    br i1 [[TMP3]], label [[COMMON_RET]], label 
[[RESOLVER_ELSE2]]
+// CHECK:       resolver_else2:
+// CHECK-NEXT:    [[TMP4:%.*]] = and i64 [[TMP0]], 1073807616
+// CHECK-NEXT:    [[TMP5:%.*]] = icmp eq i64 [[TMP4]], 1073807616
+// CHECK-NEXT:    [[FOO__MSVE_FOO_DEFAULT]] = select i1 [[TMP5]], ptr 
@foo._Msve, ptr @foo.default
+// CHECK-NEXT:    br label [[COMMON_RET]]
+//
+//
+// CHECK: Function Attrs: disable_sanitizer_instrumentation
+// CHECK-LABEL: define {{[^@]+}}@fmv_caller.resolver
+// CHECK-SAME: () #[[ATTR12]] comdat {
+// CHECK-NEXT:  resolver_entry:
+// CHECK-NEXT:    tail call void @__init_cpu_features_resolver()
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = and i64 [[TMP0]], 69793284480
+// CHECK-NEXT:    [[TMP2:%.*]] = icmp eq i64 [[TMP1]], 69793284480
+// CHECK-NEXT:    br i1 [[TMP2]], label [[COMMON_RET:%.*]], label 
[[RESOLVER_ELSE:%.*]]
+// CHECK:       common.ret:
+// CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = phi ptr [ @fmv_caller._MlseMsve2, 
[[RESOLVER_ENTRY:%.*]] ], [ @fmv_caller._Mlse, [[RESOLVER_ELSE]] ], [ 
[[FMV_CALLER__MSVE_FMV_CALLER_DEFAULT:%.*]], [[RESOLVER_ELSE2:%.*]] ]
+// CHECK-NEXT:    ret ptr [[COMMON_RET_OP]]
+// CHECK:       resolver_else:
+// CHECK-NEXT:    [[TMP3:%.*]] = and i64 [[TMP0]], 128
+// CHECK-NEXT:    [[DOTNOT:%.*]] = icmp eq i64 [[TMP3]], 0
+// CHECK-NEXT:    br i1 [[DOTNOT]], label [[RESOLVER_ELSE2]], label 
[[COMMON_RET]]
+// CHECK:       resolver_else2:
+// CHECK-NEXT:    [[TMP4:%.*]] = and i64 [[TMP0]], 1073807616
+// CHECK-NEXT:    [[TMP5:%.*]] = icmp eq i64 [[TMP4]], 1073807616
+// CHECK-NEXT:    [[FMV_CALLER__MSVE_FMV_CALLER_DEFAULT]] = select i1 
[[TMP5]], ptr @fmv_caller._Msve, ptr @fmv_caller.default
+// CHECK-NEXT:    br label [[COMMON_RET]]
+//
+//
+// CHECK: Function Attrs: disable_sanitizer_instrumentation
+// CHECK-LABEL: define {{[^@]+}}@bar.resolver
+// CHECK-SAME: () #[[ATTR12]] comdat {
+// CHECK-NEXT:  resolver_entry:
+// CHECK-NEXT:    tail call void @__init_cpu_features_resolver()
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = and i64 [[TMP0]], 800
+// CHECK-NEXT:    [[TMP2:%.*]] = icmp eq i64 [[TMP1]], 800
+// CHECK-NEXT:    [[TMP3:%.*]] = and i64 [[TMP0]], 33536
+// CHECK-NEXT:    [[TMP4:%.*]] = icmp eq i64 [[TMP3]], 33536
+// CHECK-NEXT:    [[BAR__MAES_BAR_DEFAULT:%.*]] = select i1 [[TMP4]], ptr 
@bar._Maes, ptr @bar.default
+// CHECK-NEXT:    [[COMMON_RET_OP:%.*]] = select i1 [[TMP2]], ptr @bar._Msm4, 
ptr [[BAR__MAES_BAR_DEFAULT]]
+// CHECK-NEXT:    ret ptr [[COMMON_RET_OP]]
+//
+//.
+// CHECK: attributes #[[ATTR0]] = { mustprogress nofree noinline norecurse 
nosync nounwind willreturn memory(none) "fmv-features"="P1,P2,P3,P4,lse" 
"no-trapping-math"="true" "stack-protector-buffer-size"="8" 
"target-features"="+lse" }
+// CHECK: attributes #[[ATTR1]] = { mustprogress nofree noinline norecurse 
nosync nounwind willreturn memory(none) vscale_range(1,16) 
"fmv-features"="P2,P4,sve2" "no-trapping-math"="true" 
"stack-protector-buffer-size"="8" 
"target-features"="+fp-armv8,+fullfp16,+sve,+sve2" }
+// CHECK: attributes #[[ATTR2]] = { mustprogress nofree noinline norecurse 
nosync nounwind willreturn memory(none) vscale_range(1,16) 
"fmv-features"="P1,P3,sve" "no-trapping-math"="true" 
"stack-protector-buffer-size"="8" "target-features"="+fp-armv8,+fullfp16,+sve" }
+// CHECK: attributes #[[ATTR3]] = { mustprogress nofree noinline norecurse 
nosync nounwind willreturn memory(none) "fmv-features" 
"no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+// CHECK: attributes #[[ATTR4]] = { mustprogress nofree noinline norecurse 
nosync nounwind willreturn memory(none) vscale_range(1,16) 
"fmv-features"="P0,P1,lse,sve2" "no-trapping-math"="true" 
"stack-protector-buffer-size"="8" 
"target-features"="+fp-armv8,+fullfp16,+lse,+sve,+sve2" }
+// CHECK: attributes #[[ATTR5]] = { mustprogress nofree noinline norecurse 
nosync nounwind willreturn memory(none) vscale_range(1,16) 
"fmv-features"="P1,lse" "no-trapping-math"="true" 
"stack-protector-buffer-size"="8" "target-features"="+lse" }
+// CHECK: attributes #[[ATTR6]] = { noinline nounwind vscale_range(1,16) 
"fmv-features"="P0,sve" "no-trapping-math"="true" 
"stack-protector-buffer-size"="8" "target-features"="+fp-armv8,+fullfp16,+sve" }
+// CHECK: attributes #[[ATTR7]] = { mustprogress nofree noinline norecurse 
nosync nounwind willreturn memory(none) vscale_range(1,16) "fmv-features" 
"no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+// CHECK: attributes #[[ATTR8]] = { mustprogress nofree noinline norecurse 
nosync nounwind willreturn memory(none) "fmv-features"="aes" 
"no-trapping-math"="true" "stack-protector-buffer-size"="8" 
"target-features"="+aes,+fp-armv8,+neon" }
+// CHECK: attributes #[[ATTR9]] = { mustprogress nofree noinline norecurse 
nosync nounwind willreturn memory(none) "fmv-features"="P0,P2,sm4" 
"no-trapping-math"="true" "stack-protector-buffer-size"="8" 
"target-features"="+fp-armv8,+neon,+sm4" }
+// CHECK: attributes #[[ATTR10]] = { noinline nounwind 
"no-trapping-math"="true" "stack-protector-buffer-size"="8" 
"target-features"="+aes,+fp-armv8,+neon" }
+// CHECK: attributes #[[ATTR11]] = { mustprogress nofree noinline norecurse 
nosync nounwind willreturn memory(none) "no-trapping-math"="true" 
"stack-protector-buffer-size"="8" "target-features"="+fp-armv8,+neon,+sm4" }
+// CHECK: attributes #[[ATTR12]] = { disable_sanitizer_instrumentation }
+// CHECK: attributes #[[ATTR13]] = { nounwind }
+//.
+// CHECK: [[META0:![0-9]+]] = !{i32 1, !"wchar_size", i32 4}
+// CHECK: [[META1:![0-9]+]] = !{!"{{.*}}clang version {{.*}}"}
+// CHECK: [[META2:![0-9]+]] = !{[[META3:![0-9]+]], [[META3]], i64 0}
+// CHECK: [[META3]] = !{!"int", [[META4:![0-9]+]], i64 0}
+// CHECK: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0}
+// CHECK: [[META5]] = !{!"Simple C/C++ TBAA"}
+//.

diff  --git a/clang/test/Sema/attr-target-clones-aarch64.c 
b/clang/test/Sema/attr-target-clones-aarch64.c
index 93d87cef54569..e468fd83b57f3 100644
--- a/clang/test/Sema/attr-target-clones-aarch64.c
+++ b/clang/test/Sema/attr-target-clones-aarch64.c
@@ -80,3 +80,15 @@ int useage(void) {
 int __attribute__((target_clones("sve2-sha3+ssbs", "sm4"))) mv_after_use(void) 
{ return 1; }
 // expected-error@+1 {{'main' cannot be a multiversioned function}}
 int __attribute__((target_clones("i8mm"))) main() { return 1; }
+
+int __attribute__((target_clones("aes + sve2 ; priority=100", "default"))) 
priority_whitespace(void) { return 0; }
+
+//expected-warning@+2 {{unsupported 'priority=10' in the 'target_clones' 
attribute string; 'target_clones' attribute ignored}}
+//expected-warning@+1 {{version list contains entries that don't impact code 
generation}}
+int __attribute__((target_clones("priority=10;aes", "default"))) 
priority_before_features(void) { return 0; }
+
+//expected-warning@+1 {{version priority '0' is outside the allowed range 
[1-255]; ignoring priority}}
+int __attribute__((target_clones("aes;priority=0", "default"))) 
priority_out_of_range(void) { return 0; }
+
+//expected-warning@+1 {{priority of default version cannot be overridden; 
ignoring priority}}
+int __attribute__((target_clones("aes", "default;priority=10"))) 
priority_default_version(void) { return 0; }

diff  --git a/clang/test/Sema/attr-target-version.c 
b/clang/test/Sema/attr-target-version.c
index d062212848daf..29dfc3e864b0c 100644
--- a/clang/test/Sema/attr-target-version.c
+++ b/clang/test/Sema/attr-target-version.c
@@ -117,3 +117,14 @@ int unspec_args_implicit_default_first();
 int __attribute__((target_version("aes"))) 
unspec_args_implicit_default_first() { return -1; }
 // expected-note@+1 {{function multiversioning caused by this declaration}}
 int __attribute__((target_version("default"))) 
unspec_args_implicit_default_first() { return 0; }
+
+int __attribute__((target_version("aes + sve2 ; priority=100"))) 
priority_whitespace(void) { return 0; }
+
+//expected-warning@+1 {{unsupported 'priority=10' in the 'target_version' 
attribute string; 'target_version' attribute ignored}}
+int __attribute__((target_version("priority=10;aes"))) 
priority_before_features(void) { return 0; }
+
+//expected-warning@+1 {{version priority '256' is outside the allowed range 
[1-255]; ignoring priority}}
+int __attribute__((target_version("aes;priority=256"))) 
priority_out_of_range(void) { return 0; }
+
+//expected-warning@+1 {{priority of default version cannot be overridden; 
ignoring priority}}
+int __attribute__((target_version("default;priority=10"))) 
priority_default_version(void) { return 0; }

diff  --git a/llvm/include/llvm/Analysis/TargetTransformInfo.h 
b/llvm/include/llvm/Analysis/TargetTransformInfo.h
index 99525607f744a..19d5fb0feb73e 100644
--- a/llvm/include/llvm/Analysis/TargetTransformInfo.h
+++ b/llvm/include/llvm/Analysis/TargetTransformInfo.h
@@ -1975,9 +1975,13 @@ class TargetTransformInfo {
   LLVM_ABI bool hasArmWideBranch(bool Thumb) const;
 
   /// Returns a bitmask constructed from the target-features or fmv-features
-  /// metadata of a function.
+  /// metadata of a function corresponding to its Arch Extensions.
   LLVM_ABI APInt getFeatureMask(const Function &F) const;
 
+  /// Returns a bitmask constructed from the target-features or fmv-features
+  /// metadata of a function corresponding to its FMV priority.
+  LLVM_ABI APInt getPriorityMask(const Function &F) const;
+
   /// Returns true if this is an instance of a function with multiple versions.
   LLVM_ABI bool isMultiversionedFunction(const Function &F) const;
 

diff  --git a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h 
b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
index 835eb7701ccfa..da104ffe0a6e6 100644
--- a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
+++ b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
@@ -1130,6 +1130,10 @@ class TargetTransformInfoImplBase {
     return APInt::getZero(32);
   }
 
+  virtual APInt getPriorityMask(const Function &F) const {
+    return APInt::getZero(32);
+  }
+
   virtual bool isMultiversionedFunction(const Function &F) const {
     return false;
   }

diff  --git a/llvm/include/llvm/TargetParser/AArch64FeatPriorities.inc 
b/llvm/include/llvm/TargetParser/AArch64FeatPriorities.inc
index f2bad28ada93e..f0291926bbe75 100644
--- a/llvm/include/llvm/TargetParser/AArch64FeatPriorities.inc
+++ b/llvm/include/llvm/TargetParser/AArch64FeatPriorities.inc
@@ -59,7 +59,16 @@ enum FeatPriorities {
   PRIOR_SME_I64,
   PRIOR_SME2,
   PRIOR_MOPS,
-  PRIOR_CSSC
+  PRIOR_CSSC,
+  PRIOR_MAX,
+  PRIOR_P0 = 120,
+  PRIOR_P1,
+  PRIOR_P2,
+  PRIOR_P3,
+  PRIOR_P4,
+  PRIOR_P5,
+  PRIOR_P6,
+  PRIOR_P7
 };
 
 #endif

diff  --git a/llvm/include/llvm/TargetParser/AArch64TargetParser.h 
b/llvm/include/llvm/TargetParser/AArch64TargetParser.h
index 7da529e2e8a87..224bcaf79412d 100644
--- a/llvm/include/llvm/TargetParser/AArch64TargetParser.h
+++ b/llvm/include/llvm/TargetParser/AArch64TargetParser.h
@@ -41,6 +41,8 @@ struct CpuInfo;
 static_assert(FEAT_MAX < 62,
               "Number of features in CPUFeatures are limited to 62 entries");
 
+static_assert(PRIOR_MAX < 120, "FeatPriorities is limited to 120 entries");
+
 // Each ArchExtKind correponds directly to a possible -target-feature.
 #define EMIT_ARCHEXTKIND_ENUM
 #include "llvm/TargetParser/AArch64TargetParserDef.inc"
@@ -71,11 +73,12 @@ struct ExtensionInfo {
 
 struct FMVInfo {
   StringRef Name;                // The target_version/target_clones spelling.
-  CPUFeatures FeatureBit;        // Index of the bit in the FMV feature bitset.
+  std::optional<CPUFeatures>
+      FeatureBit;                // Index of the bit in the FMV feature bitset.
   FeatPriorities PriorityBit;    // Index of the bit in the FMV priority 
bitset.
   std::optional<ArchExtKind> ID; // The architecture extension to enable.
-  FMVInfo(StringRef Name, CPUFeatures FeatureBit, FeatPriorities PriorityBit,
-          std::optional<ArchExtKind> ID)
+  FMVInfo(StringRef Name, std::optional<CPUFeatures> FeatureBit,
+          FeatPriorities PriorityBit, std::optional<ArchExtKind> ID)
       : Name(Name), FeatureBit(FeatureBit), PriorityBit(PriorityBit), ID(ID) 
{};
 };
 

diff  --git a/llvm/lib/Analysis/TargetTransformInfo.cpp 
b/llvm/lib/Analysis/TargetTransformInfo.cpp
index c529d87502acd..6201f6a2acbf3 100644
--- a/llvm/lib/Analysis/TargetTransformInfo.cpp
+++ b/llvm/lib/Analysis/TargetTransformInfo.cpp
@@ -1434,6 +1434,10 @@ APInt TargetTransformInfo::getFeatureMask(const Function 
&F) const {
   return TTIImpl->getFeatureMask(F);
 }
 
+APInt TargetTransformInfo::getPriorityMask(const Function &F) const {
+  return TTIImpl->getPriorityMask(F);
+}
+
 bool TargetTransformInfo::isMultiversionedFunction(const Function &F) const {
   return TTIImpl->isMultiversionedFunction(F);
 }

diff  --git a/llvm/lib/Target/AArch64/AArch64FMV.td 
b/llvm/lib/Target/AArch64/AArch64FMV.td
index b0f76ec6a6480..12939997401ac 100644
--- a/llvm/lib/Target/AArch64/AArch64FMV.td
+++ b/llvm/lib/Target/AArch64/AArch64FMV.td
@@ -83,3 +83,14 @@ def : FMVExtension<"sve2-sha3", "SVE_SHA3">;
 def : FMVExtension<"sve2-sm4", "SVE_SM4">;
 def : FMVExtension<"wfxt", "WFXT">;
 def : FMVExtension<"cssc", "CSSC">;
+
+// Extensions which allow the user to override version priority.
+// 8-bits allow 256-1 priority levels (excluding all zeros).
+def : FMVExtension<"P0", "P0">;
+def : FMVExtension<"P1", "P1">;
+def : FMVExtension<"P2", "P2">;
+def : FMVExtension<"P3", "P3">;
+def : FMVExtension<"P4", "P4">;
+def : FMVExtension<"P5", "P5">;
+def : FMVExtension<"P6", "P6">;
+def : FMVExtension<"P7", "P7">;

diff  --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp 
b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
index 4bc98c742c33f..043be554f8441 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp
@@ -252,12 +252,23 @@ static bool hasPossibleIncompatibleOps(const Function *F,
   return false;
 }
 
-APInt AArch64TTIImpl::getFeatureMask(const Function &F) const {
+static void extractAttrFeatures(const Function &F, const AArch64TTIImpl *TTI,
+                                SmallVectorImpl<StringRef> &Features) {
   StringRef AttributeStr =
-      isMultiversionedFunction(F) ? "fmv-features" : "target-features";
+      TTI->isMultiversionedFunction(F) ? "fmv-features" : "target-features";
   StringRef FeatureStr = F.getFnAttribute(AttributeStr).getValueAsString();
-  SmallVector<StringRef, 8> Features;
   FeatureStr.split(Features, ",");
+}
+
+APInt AArch64TTIImpl::getFeatureMask(const Function &F) const {
+  SmallVector<StringRef, 8> Features;
+  extractAttrFeatures(F, this, Features);
+  return AArch64::getCpuSupportsMask(Features);
+}
+
+APInt AArch64TTIImpl::getPriorityMask(const Function &F) const {
+  SmallVector<StringRef, 8> Features;
+  extractAttrFeatures(F, this, Features);
   return AArch64::getFMVPriority(Features);
 }
 

diff  --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h 
b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h
index 7ca8482e152d1..ecefe2a7f1380 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h
+++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h
@@ -98,6 +98,7 @@ class AArch64TTIImpl final : public 
BasicTTIImplBase<AArch64TTIImpl> {
                                 unsigned DefaultCallPenalty) const override;
 
   APInt getFeatureMask(const Function &F) const override;
+  APInt getPriorityMask(const Function &F) const override;
 
   bool isMultiversionedFunction(const Function &F) const override;
 

diff  --git a/llvm/lib/TargetParser/AArch64TargetParser.cpp 
b/llvm/lib/TargetParser/AArch64TargetParser.cpp
index 7e3583275a734..2c0211b3a2919 100644
--- a/llvm/lib/TargetParser/AArch64TargetParser.cpp
+++ b/llvm/lib/TargetParser/AArch64TargetParser.cpp
@@ -55,21 +55,30 @@ std::optional<AArch64::FMVInfo> 
lookupFMVByID(AArch64::ArchExtKind ExtID) {
   return {};
 }
 
+std::optional<AArch64::FMVInfo> getFMVInfoFrom(StringRef Feature) {
+  std::optional<AArch64::FMVInfo> FMV = AArch64::parseFMVExtension(Feature);
+  if (!FMV && Feature.starts_with('+'))
+    if (std::optional<AArch64::ExtensionInfo> Ext =
+            AArch64::targetFeatureToExtension(Feature))
+      FMV = lookupFMVByID(Ext->ID);
+  return FMV;
+}
+
 APInt AArch64::getFMVPriority(ArrayRef<StringRef> Features) {
   // Transitively enable the Arch Extensions which correspond to each feature.
   ExtensionSet FeatureBits;
+  APInt PriorityMask = APInt::getZero(128);
   for (const StringRef Feature : Features) {
-    std::optional<FMVInfo> FMV = parseFMVExtension(Feature);
-    if (!FMV && Feature.starts_with('+')) {
-      if (std::optional<ExtensionInfo> Info = 
targetFeatureToExtension(Feature))
-        FMV = lookupFMVByID(Info->ID);
+    if (std::optional<FMVInfo> FMV = getFMVInfoFrom(Feature)) {
+      // FMV feature without a corresponding Arch Extension may affect priority
+      if (FMV->ID)
+        FeatureBits.enable(*FMV->ID);
+      else
+        PriorityMask.setBit(FMV->PriorityBit);
     }
-    if (FMV && FMV->ID)
-      FeatureBits.enable(*FMV->ID);
   }
 
   // Construct a bitmask for all the transitively enabled Arch Extensions.
-  APInt PriorityMask = APInt::getZero(128);
   for (const FMVInfo &Info : getFMVInfo())
     if (Info.ID && FeatureBits.Enabled.test(*Info.ID))
       PriorityMask.setBit(Info.PriorityBit);
@@ -81,15 +90,15 @@ APInt AArch64::getCpuSupportsMask(ArrayRef<StringRef> 
Features) {
   // Transitively enable the Arch Extensions which correspond to each feature.
   ExtensionSet FeatureBits;
   for (const StringRef Feature : Features)
-    if (std::optional<FMVInfo> Info = parseFMVExtension(Feature))
-      if (Info->ID)
-        FeatureBits.enable(*Info->ID);
+    if (std::optional<FMVInfo> FMV = getFMVInfoFrom(Feature))
+      if (FMV->ID)
+        FeatureBits.enable(*FMV->ID);
 
   // Construct a bitmask for all the transitively enabled Arch Extensions.
   APInt FeaturesMask = APInt::getZero(128);
   for (const FMVInfo &Info : getFMVInfo())
     if (Info.ID && FeatureBits.Enabled.test(*Info.ID))
-      FeaturesMask.setBit(Info.FeatureBit);
+      FeaturesMask.setBit(*Info.FeatureBit);
 
   return FeaturesMask;
 }

diff  --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp 
b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index 939071725253f..66b95fc7bde5f 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -2546,6 +2546,8 @@ static bool OptimizeNonTrivialIFuncs(
 
   // Map containing the feature bits for a given function.
   DenseMap<Function *, APInt> FeatureMask;
+  // Map containing the priority bits for a given function.
+  DenseMap<Function *, APInt> PriorityMask;
   // Map containing all the function versions corresponding to an IFunc symbol.
   DenseMap<GlobalIFunc *, SmallVector<Function *>> VersionedFuncs;
   // Map containing the IFunc symbol a function is version of.
@@ -2581,14 +2583,17 @@ static bool OptimizeNonTrivialIFuncs(
 
     for (Function *V : Versions) {
       VersionOf.insert({V, &IF});
-      auto [It, Inserted] = FeatureMask.try_emplace(V);
-      if (Inserted)
-        It->second = GetTTI(*V).getFeatureMask(*V);
+      auto [FeatIt, FeatInserted] = FeatureMask.try_emplace(V);
+      if (FeatInserted)
+        FeatIt->second = GetTTI(*V).getFeatureMask(*V);
+      auto [PriorIt, PriorInserted] = PriorityMask.try_emplace(V);
+      if (PriorInserted)
+        PriorIt->second = GetTTI(*V).getPriorityMask(*V);
     }
 
     // Sort function versions in decreasing priority order.
     sort(Versions, [&](auto *LHS, auto *RHS) {
-      return FeatureMask[LHS].ugt(FeatureMask[RHS]);
+      return PriorityMask[LHS].ugt(PriorityMask[RHS]);
     });
 
     IFuncs.push_back(&IF);

diff  --git a/llvm/test/Transforms/GlobalOpt/resolve-fmv-ifunc.ll 
b/llvm/test/Transforms/GlobalOpt/resolve-fmv-ifunc.ll
index a7fcc667dbedc..32709cb2cc035 100644
--- a/llvm/test/Transforms/GlobalOpt/resolve-fmv-ifunc.ll
+++ b/llvm/test/Transforms/GlobalOpt/resolve-fmv-ifunc.ll
@@ -1,4 +1,4 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py 
UTC_ARGS: --filter "call i32 
@(test_single_bb_resolver|test_multi_bb_resolver|test_caller_feats_not_implied|test_non_fmv_caller|test_priority|test_alternative_names|test_unrelated_callers|test_known_bits)"
 --version 4
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py 
UTC_ARGS: --filter "call i32 
@(test_single_bb_resolver|test_multi_bb_resolver|test_caller_feats_not_implied|test_non_fmv_caller|test_priority|test_alternative_names|test_unrelated_callers|test_known_bits|test_priority_override)"
 --version 4
 
 ; REQUIRES: aarch64-registered-target
 
@@ -15,6 +15,7 @@ $test_priority.resolver = comdat any
 $test_alternative_names.resolver = comdat any
 $test_unrelated_callers.resolver = comdat any
 $test_known_bits.resolver = comdat any
+$test_priority_override.resolver = comdat any
 $caller1.resolver = comdat any
 $caller2.resolver = comdat any
 $caller3.resolver = comdat any
@@ -23,6 +24,7 @@ $caller7.resolver = comdat any
 $caller8.resolver = comdat any
 $caller9.resolver = comdat any
 $caller11.resolver = comdat any
+$caller12.resolver = comdat any
 
 @__aarch64_cpu_features = external local_unnamed_addr global { i64 }
 
@@ -34,6 +36,7 @@ $caller11.resolver = comdat any
 @test_alternative_names = weak_odr ifunc i32 (), ptr 
@test_alternative_names.resolver
 @test_unrelated_callers = weak_odr ifunc i32 (), ptr 
@test_unrelated_callers.resolver
 @test_known_bits = weak_odr ifunc i32 (), ptr @test_known_bits.resolver
+@test_priority_override = weak_odr ifunc i32 (), ptr 
@test_priority_override.resolver
 @caller1 = weak_odr ifunc i32 (), ptr @caller1.resolver
 @caller2 = weak_odr ifunc i32 (), ptr @caller2.resolver
 @caller3 = weak_odr ifunc i32 (), ptr @caller3.resolver
@@ -42,6 +45,7 @@ $caller11.resolver = comdat any
 @caller8 = weak_odr ifunc i32 (), ptr @caller8.resolver
 @caller9 = weak_odr ifunc i32 (), ptr @caller9.resolver
 @caller11 = weak_odr ifunc i32 (), ptr @caller11.resolver
+@caller12 = weak_odr ifunc i32 (), ptr @caller12.resolver
 
 declare void @__init_cpu_features_resolver() local_unnamed_addr
 
@@ -678,6 +682,68 @@ resolver_else2:                                   ; preds 
= %resolver_else
   br label %common.ret
 }
 
+declare i32 @test_priority_override._Maes() #20
+declare i32 @test_priority_override._Msme() #21
+declare i32 @test_priority_override.default() #0
+
+define weak_odr ptr @test_priority_override.resolver() comdat {
+; CHECK-LABEL: define weak_odr ptr @test_priority_override.resolver() comdat {
+resolver_entry:
+  tail call void @__init_cpu_features_resolver()
+  %0 = load i64, ptr @__aarch64_cpu_features, align 8
+  %1 = and i64 %0, 33536
+  %2 = icmp eq i64 %1, 33536
+  %3 = and i64 %0, 4398180795136
+  %4 = icmp eq i64 %3, 4398180795136
+  %test_priority_override._Msme.test_priority_override.default = select i1 %4, 
ptr @test_priority_override._Msme, ptr @test_priority_override.default
+  %common.ret.op = select i1 %2, ptr @test_priority_override._Maes, ptr 
%test_priority_override._Msme.test_priority_override.default
+  ret ptr %common.ret.op
+}
+
+define i32 @caller12._Maes() #22 {
+; CHECK-LABEL: define i32 @caller12._Maes(
+; CHECK-SAME: ) #[[ATTR6:[0-9]+]] {
+; CHECK:    [[CALL:%.*]] = tail call i32 @test_priority_override._Maes()
+;
+entry:
+  %call = tail call i32 @test_priority_override()
+  ret i32 %call
+}
+
+define i32 @caller12._Mbf16() #23 {
+; CHECK-LABEL: define i32 @caller12._Mbf16(
+; CHECK-SAME: ) #[[ATTR19:[0-9]+]] {
+; CHECK:    [[CALL:%.*]] = tail call i32 @test_priority_override()
+;
+entry:
+  %call = tail call i32 @test_priority_override()
+  ret i32 %call
+}
+
+define noundef i32 @caller12.default() #0 {
+; CHECK-LABEL: define noundef i32 @caller12.default(
+; CHECK-SAME: ) #[[ATTR0]] {
+; CHECK:    [[CALL:%.*]] = tail call i32 @test_priority_override.default()
+;
+entry:
+  %call = tail call i32 @test_priority_override()
+  ret i32 %call
+}
+
+define weak_odr ptr @caller12.resolver() comdat {
+; CHECK-LABEL: define weak_odr ptr @caller12.resolver() comdat {
+resolver_entry:
+  tail call void @__init_cpu_features_resolver()
+  %0 = load i64, ptr @__aarch64_cpu_features, align 8
+  %1 = and i64 %0, 33536
+  %2 = icmp eq i64 %1, 33536
+  %3 = and i64 %0, 134218496
+  %4 = icmp eq i64 %3, 134218496
+  %caller12._Mbf16.caller12.default = select i1 %4, ptr @caller12._Mbf16, ptr 
@caller12.default
+  %common.ret.op = select i1 %2, ptr @caller12._Maes, ptr 
%caller12._Mbf16.caller12.default
+  ret ptr %common.ret.op
+}
+
 attributes #0 = { "fmv-features" }
 attributes #1 = { "fmv-features"="sve" }
 attributes #2 = { "fmv-features"="sve2" }
@@ -698,3 +764,7 @@ attributes #16 = { "fmv-features"="rcpc2" }
 attributes #17 = { "fmv-features"="frintts" }
 attributes #18 = { 
"target-features"="+fp-armv8,+mops,+neon,+outline-atomics,+sve,+v8a" }
 attributes #19 = { "fmv-features"="aes,sve2" }
+attributes #20 = { "fmv-features"="P1,P4,P5,aes" } ; priority=50
+attributes #21 = { "fmv-features"="P1,P2,P3,P4,sme" } ; priority=30
+attributes #22 = { "fmv-features"="P3,P6,P7,aes" } ; priority=200
+attributes #23 = { "fmv-features"="P2,P5,P6,bf16" } ; priority=100

diff  --git a/llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp 
b/llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp
index b63ce3671f922..a18fed082207f 100644
--- a/llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp
@@ -159,12 +159,15 @@ static void emitARMTargetDef(const RecordKeeper &RK, 
raw_ostream &OS) {
      << "  if(I.size()) return I;\n"
      << "  I.reserve(" << FMVExts.size() << ");\n";
   for (const Record *Rec : FMVExts) {
+    auto FeatName = Rec->getValueAsString("BackendFeature");
+    const Record *FeatRec = ExtensionMap[FeatName];
     OS << "  I.emplace_back(";
     OS << "\"" << Rec->getValueAsString("Name") << "\"";
-    OS << ", " << Rec->getValueAsString("FeatureBit");
+    if (FeatRec)
+      OS << ", " << Rec->getValueAsString("FeatureBit");
+    else
+      OS << ", std::nullopt";
     OS << ", " << Rec->getValueAsString("PriorityBit");
-    auto FeatName = Rec->getValueAsString("BackendFeature");
-    const Record *FeatRec = ExtensionMap[FeatName];
     if (FeatRec)
       OS << ", " << FeatRec->getValueAsString("ArchExtKindSpelling").upper();
     else


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to