-
Notifications
You must be signed in to change notification settings - Fork 15.5k
[clang][FMV][AArch64] Diagnose/ignore unreachable functions versions. #171496
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[clang][FMV][AArch64] Diagnose/ignore unreachable functions versions. #171496
Conversation
The commit llvm#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.
|
@llvm/pr-subscribers-clang-codegen @llvm/pr-subscribers-clang Author: Alexandros Lamprineas (labrinea) ChangesThe commit #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. Full diff: https://github.com/llvm/llvm-project/pull/171496.diff 4 Files Affected:
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 {{.*}}"}
+//.
|
The commit #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.