https://github.com/labrinea created 
https://github.com/llvm/llvm-project/pull/171496

The commit https://github.com/llvm/llvm-project/pull/150267 allows the user to 
override version priority. As a result it is now possible to define an 
unreachable function version if a higher priority version contains a subset of 
its FMV features. For example:

target_clones("sve;priority=2", "sve2;priority=1")

the sve2 version is unreachable, since if you don't have sve we can't have sve2 
either.

The patch emits a warning about such cases and ignores those versions when 
generating the resolver. Also removes their definitions.

>From ebb9498f0f656870dca99c359f7f959c4e4cae4c Mon Sep 17 00:00:00 2001
From: Alexandros Lamprineas <[email protected]>
Date: Tue, 9 Dec 2025 19:45:44 +0000
Subject: [PATCH] [clang][FMV][AArch64] Diagnose/ignore unreachable functions
 versions.

The commit https://github.com/llvm/llvm-project/pull/150267 allows
the user to override version priority. As a result it is now possible
to define an unreachable function version if a higher priority version
contains a subset of its FMV features. For example:

target_clones("sve;priority=2", "sve2;priority=1")

the sve2 version is unreachable, since if you don't have sve we can't
have sve2 either.

The patch emits a warning about such cases and ignores those versions
when generating the resolver. Also removes their definitions.
---
 .../clang/Basic/DiagnosticFrontendKinds.td    |  4 +
 clang/lib/CodeGen/CodeGenFunction.cpp         |  4 +
 clang/lib/CodeGen/CodeGenModule.cpp           | 23 +++++
 .../CodeGen/AArch64/fmv-unreachable-version.c | 89 +++++++++++++++++++
 4 files changed, 120 insertions(+)
 create mode 100644 clang/test/CodeGen/AArch64/fmv-unreachable-version.c

diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td 
b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 9e344160ff934..a2d4564e23285 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -399,6 +399,10 @@ def err_invalid_llvm_ir : Error<"invalid LLVM IR input: 
%0">;
 def err_os_unsupport_riscv_fmv : Error<
   "function multiversioning is currently only supported on Linux">;
 
+def warn_unreachable_version
+    : Warning<"function version '%0' is unreachable; ignoring version">,
+      InGroup<FunctionMultiVersioning>;
+
 def warn_hlsl_langstd_minimal :
   Warning<"support for HLSL language version %0 is incomplete, "
           "recommend using %1 instead">,
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp 
b/clang/lib/CodeGen/CodeGenFunction.cpp
index ac25bd95f0463..2bf2fd68b860e 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -3143,6 +3143,10 @@ void CodeGenFunction::EmitAArch64MultiVersionResolver(
       Builder.SetInsertPoint(CurBlock);
     }
 
+    // Skip unreachable versions.
+    if (RO.Function == nullptr)
+      continue;
+
     llvm::BasicBlock *RetBlock = createBasicBlock("resolver_return", Resolver);
     CGBuilderTy RetBuilder(*this, RetBlock);
     CreateMultiVersionResolverReturn(CGM, Resolver, RetBuilder, RO.Function,
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index 1dcf94fc35e07..9864c4cc03274 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -68,6 +68,7 @@
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/Hash.h"
 #include "llvm/Support/TimeProfiler.h"
+#include "llvm/TargetParser/AArch64TargetParser.h"
 #include "llvm/TargetParser/RISCVISAInfo.h"
 #include "llvm/TargetParser/Triple.h"
 #include "llvm/TargetParser/X86TargetParser.h"
@@ -4674,6 +4675,7 @@ void CodeGenModule::emitMultiVersionFunctions() {
     // in this TU. For other architectures it is always emitted.
     bool ShouldEmitResolver = !getTarget().getTriple().isAArch64();
     SmallVector<CodeGenFunction::FMVResolverOption, 10> Options;
+    llvm::DenseMap<llvm::Function *, const FunctionDecl *> DeclMap;
 
     getContext().forEachMultiversionedFunctionVersion(
         FD, [&](const FunctionDecl *CurFD) {
@@ -4684,11 +4686,13 @@ void CodeGenModule::emitMultiVersionFunctions() {
             assert(getTarget().getTriple().isX86() && "Unsupported target");
             TA->getX86AddedFeatures(Feats);
             llvm::Function *Func = createFunction(CurFD);
+            DeclMap.insert({Func, CurFD});
             Options.emplace_back(Func, Feats, TA->getX86Architecture());
           } else if (const auto *TVA = CurFD->getAttr<TargetVersionAttr>()) {
             if (TVA->isDefaultVersion() && IsDefined)
               ShouldEmitResolver = true;
             llvm::Function *Func = createFunction(CurFD);
+            DeclMap.insert({Func, CurFD});
             char Delim = getTarget().getTriple().isAArch64() ? '+' : ',';
             TVA->getFeatures(Feats, Delim);
             Options.emplace_back(Func, Feats);
@@ -4699,6 +4703,7 @@ void CodeGenModule::emitMultiVersionFunctions() {
               if (TC->isDefaultVersion(I) && IsDefined)
                 ShouldEmitResolver = true;
               llvm::Function *Func = createFunction(CurFD, I);
+              DeclMap.insert({Func, CurFD});
               Feats.clear();
               if (getTarget().getTriple().isX86()) {
                 TC->getX86Feature(Feats, I);
@@ -4744,6 +4749,24 @@ void CodeGenModule::emitMultiVersionFunctions() {
                        const CodeGenFunction::FMVResolverOption &RHS) {
           return getFMVPriority(TI, LHS).ugt(getFMVPriority(TI, RHS));
         });
+
+    // Diagnose unreachable function versions.
+    if (getTarget().getTriple().isAArch64()) {
+      for (auto I = Options.begin() + 1, E = Options.end(); I != E; ++I) {
+        llvm::APInt RHS = llvm::AArch64::getCpuSupportsMask(I->Features);
+        if (std::any_of(Options.begin(), I, [RHS](auto RO) {
+              llvm::APInt LHS = llvm::AArch64::getCpuSupportsMask(RO.Features);
+              return LHS.isSubsetOf(RHS);
+            })) {
+          Diags.Report(DeclMap[I->Function]->getLocation(),
+                       diag::warn_unreachable_version)
+              << I->Function->getName();
+          assert(I->Function->user_empty() && "unexpected users");
+          I->Function->eraseFromParent();
+          I->Function = nullptr;
+        }
+      }
+    }
     CodeGenFunction CGF(*this);
     CGF.EmitMultiVersionResolver(ResolverFunc, Options);
 
diff --git a/clang/test/CodeGen/AArch64/fmv-unreachable-version.c 
b/clang/test/CodeGen/AArch64/fmv-unreachable-version.c
new file mode 100644
index 0000000000000..c9626c5ef34f3
--- /dev/null
+++ b/clang/test/CodeGen/AArch64/fmv-unreachable-version.c
@@ -0,0 +1,89 @@
+// 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-linux-gnu -verify -emit-llvm -o - %s | 
FileCheck %s
+
+__attribute__((target_version("sve;priority=5"))) int 
unreachable_versions(void) { return 5; }
+// expected-warning@+1 {{function version 'unreachable_versions._MlseMmops' is 
unreachable; ignoring version}}
+__attribute__((target_version("mops+lse;priority=1"))) int 
unreachable_versions(void) { return 1; }
+int foo() { return unreachable_versions(); }
+// expected-warning@+1 {{function version 'unreachable_versions._Msve2' is 
unreachable; ignoring version}}
+__attribute__((target_clones("sve2;priority=4", "aes+sve2;priority=3", 
"lse;priority=2", "default"))) int unreachable_versions(void) { return 0; }
+// expected-warning@-1 {{function version 'unreachable_versions._MaesMsve2' is 
unreachable; ignoring version}}
+
+//.
+// CHECK: @__aarch64_cpu_features = external dso_local global { i64 }
+// CHECK: @unreachable_versions = weak_odr ifunc i32 (), ptr 
@unreachable_versions.resolver
+//.
+// CHECK: Function Attrs: noinline nounwind optnone vscale_range(1,16)
+// CHECK-LABEL: define {{[^@]+}}@unreachable_versions._Msve
+// CHECK-SAME: () #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    ret i32 5
+//
+//
+// CHECK: Function Attrs: noinline nounwind optnone
+// CHECK-LABEL: define {{[^@]+}}@foo
+// CHECK-SAME: () #[[ATTR1:[0-9]+]] {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[CALL:%.*]] = call i32 @unreachable_versions()
+// CHECK-NEXT:    ret i32 [[CALL]]
+//
+//
+// CHECK: Function Attrs: noinline nounwind optnone vscale_range(1,16)
+// CHECK-LABEL: define {{[^@]+}}@unreachable_versions._Mlse
+// CHECK-SAME: () #[[ATTR2:[0-9]+]] {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    ret i32 0
+//
+//
+// CHECK: Function Attrs: noinline nounwind optnone vscale_range(1,16)
+// CHECK-LABEL: define {{[^@]+}}@unreachable_versions.default
+// CHECK-SAME: () #[[ATTR3:[0-9]+]] {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    ret i32 0
+//
+//
+// CHECK: Function Attrs: disable_sanitizer_instrumentation
+// CHECK-LABEL: define {{[^@]+}}@unreachable_versions.resolver
+// CHECK-SAME: () #[[ATTR4:[0-9]+]] comdat {
+// CHECK-NEXT:  resolver_entry:
+// CHECK-NEXT:    call void @__init_cpu_features_resolver()
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = and i64 [[TMP0]], 1073807616
+// CHECK-NEXT:    [[TMP2:%.*]] = icmp eq i64 [[TMP1]], 1073807616
+// CHECK-NEXT:    [[TMP3:%.*]] = and i1 true, [[TMP2]]
+// CHECK-NEXT:    br i1 [[TMP3]], label [[RESOLVER_RETURN:%.*]], label 
[[RESOLVER_ELSE:%.*]]
+// CHECK:       resolver_return:
+// CHECK-NEXT:    ret ptr @unreachable_versions._Msve
+// CHECK:       resolver_else:
+// CHECK-NEXT:    [[TMP4:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = and i64 [[TMP4]], 69793284352
+// CHECK-NEXT:    [[TMP6:%.*]] = icmp eq i64 [[TMP5]], 69793284352
+// CHECK-NEXT:    [[TMP7:%.*]] = and i1 true, [[TMP6]]
+// CHECK-NEXT:    [[TMP8:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
+// CHECK-NEXT:    [[TMP9:%.*]] = and i64 [[TMP8]], 69793317632
+// CHECK-NEXT:    [[TMP10:%.*]] = icmp eq i64 [[TMP9]], 69793317632
+// CHECK-NEXT:    [[TMP11:%.*]] = and i1 true, [[TMP10]]
+// CHECK-NEXT:    [[TMP12:%.*]] = load i64, ptr @__aarch64_cpu_features, align 
8
+// CHECK-NEXT:    [[TMP13:%.*]] = and i64 [[TMP12]], 128
+// CHECK-NEXT:    [[TMP14:%.*]] = icmp eq i64 [[TMP13]], 128
+// CHECK-NEXT:    [[TMP15:%.*]] = and i1 true, [[TMP14]]
+// CHECK-NEXT:    br i1 [[TMP15]], label [[RESOLVER_RETURN1:%.*]], label 
[[RESOLVER_ELSE2:%.*]]
+// CHECK:       resolver_return1:
+// CHECK-NEXT:    ret ptr @unreachable_versions._Mlse
+// CHECK:       resolver_else2:
+// CHECK-NEXT:    [[TMP16:%.*]] = load i64, ptr @__aarch64_cpu_features, align 
8
+// CHECK-NEXT:    [[TMP17:%.*]] = and i64 [[TMP16]], 576460752303423616
+// CHECK-NEXT:    [[TMP18:%.*]] = icmp eq i64 [[TMP17]], 576460752303423616
+// CHECK-NEXT:    [[TMP19:%.*]] = and i1 true, [[TMP18]]
+// CHECK-NEXT:    ret ptr @unreachable_versions.default
+//
+//.
+// CHECK: attributes #[[ATTR0]] = { noinline nounwind optnone 
vscale_range(1,16) "fmv-features"="P0,P2,sve" "no-trapping-math"="true" 
"stack-protector-buffer-size"="8" "target-features"="+fp-armv8,+fullfp16,+sve" }
+// CHECK: attributes #[[ATTR1]] = { noinline nounwind optnone 
"no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+// CHECK: attributes #[[ATTR2]] = { noinline nounwind optnone 
vscale_range(1,16) "fmv-features"="P1,lse" "no-trapping-math"="true" 
"stack-protector-buffer-size"="8" "target-features"="+lse" }
+// CHECK: attributes #[[ATTR3]] = { noinline nounwind optnone 
vscale_range(1,16) "fmv-features" "no-trapping-math"="true" 
"stack-protector-buffer-size"="8" }
+// CHECK: attributes #[[ATTR4]] = { disable_sanitizer_instrumentation }
+//.
+// CHECK: [[META0:![0-9]+]] = !{i32 1, !"wchar_size", i32 4}
+// CHECK: [[META1:![0-9]+]] = !{!"{{.*}}clang version {{.*}}"}
+//.

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

Reply via email to