[clang] [clang][FMV][AArch64] Diagnose/ignore unreachable functions versions. (PR #171496)
Alexandros Lamprineas via cfe-commits
cfe-commits at lists.llvm.org
Tue Dec 9 12:10:09 PST 2025
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 <alexandros.lamprineas at arm.com>
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 at +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 at +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 at -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 {{.*}}"}
+//.
More information about the cfe-commits
mailing list