Skip to content

Commit e6abe81

Browse files
labrineavaladaptive
authored andcommitted
[clang][FMV][AArch64] Diagnose/ignore unreachable functions versions. (llvm#171496)
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.
1 parent 9866e38 commit e6abe81

File tree

4 files changed

+120
-0
lines changed

4 files changed

+120
-0
lines changed

clang/include/clang/Basic/DiagnosticFrontendKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,10 @@ def err_invalid_llvm_ir : Error<"invalid LLVM IR input: %0">;
398398
def err_os_unsupport_riscv_fmv : Error<
399399
"function multiversioning is currently only supported on Linux">;
400400

401+
def warn_unreachable_version
402+
: Warning<"function version '%0' is unreachable; ignoring version">,
403+
InGroup<FunctionMultiVersioning>;
404+
401405
def warn_hlsl_langstd_minimal :
402406
Warning<"support for HLSL language version %0 is incomplete, "
403407
"recommend using %1 instead">,

clang/lib/CodeGen/CodeGenFunction.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3144,6 +3144,10 @@ void CodeGenFunction::EmitAArch64MultiVersionResolver(
31443144
Builder.SetInsertPoint(CurBlock);
31453145
}
31463146

3147+
// Skip unreachable versions.
3148+
if (RO.Function == nullptr)
3149+
continue;
3150+
31473151
llvm::BasicBlock *RetBlock = createBasicBlock("resolver_return", Resolver);
31483152
CGBuilderTy RetBuilder(*this, RetBlock);
31493153
CreateMultiVersionResolverReturn(CGM, Resolver, RetBuilder, RO.Function,

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
#include "llvm/Support/ErrorHandling.h"
6969
#include "llvm/Support/Hash.h"
7070
#include "llvm/Support/TimeProfiler.h"
71+
#include "llvm/TargetParser/AArch64TargetParser.h"
7172
#include "llvm/TargetParser/RISCVISAInfo.h"
7273
#include "llvm/TargetParser/Triple.h"
7374
#include "llvm/TargetParser/X86TargetParser.h"
@@ -4664,6 +4665,7 @@ void CodeGenModule::emitMultiVersionFunctions() {
46644665
// in this TU. For other architectures it is always emitted.
46654666
bool ShouldEmitResolver = !getTarget().getTriple().isAArch64();
46664667
SmallVector<CodeGenFunction::FMVResolverOption, 10> Options;
4668+
llvm::DenseMap<llvm::Function *, const FunctionDecl *> DeclMap;
46674669

46684670
getContext().forEachMultiversionedFunctionVersion(
46694671
FD, [&](const FunctionDecl *CurFD) {
@@ -4674,11 +4676,13 @@ void CodeGenModule::emitMultiVersionFunctions() {
46744676
assert(getTarget().getTriple().isX86() && "Unsupported target");
46754677
TA->getX86AddedFeatures(Feats);
46764678
llvm::Function *Func = createFunction(CurFD);
4679+
DeclMap.insert({Func, CurFD});
46774680
Options.emplace_back(Func, Feats, TA->getX86Architecture());
46784681
} else if (const auto *TVA = CurFD->getAttr<TargetVersionAttr>()) {
46794682
if (TVA->isDefaultVersion() && IsDefined)
46804683
ShouldEmitResolver = true;
46814684
llvm::Function *Func = createFunction(CurFD);
4685+
DeclMap.insert({Func, CurFD});
46824686
char Delim = getTarget().getTriple().isAArch64() ? '+' : ',';
46834687
TVA->getFeatures(Feats, Delim);
46844688
Options.emplace_back(Func, Feats);
@@ -4689,6 +4693,7 @@ void CodeGenModule::emitMultiVersionFunctions() {
46894693
if (TC->isDefaultVersion(I) && IsDefined)
46904694
ShouldEmitResolver = true;
46914695
llvm::Function *Func = createFunction(CurFD, I);
4696+
DeclMap.insert({Func, CurFD});
46924697
Feats.clear();
46934698
if (getTarget().getTriple().isX86()) {
46944699
TC->getX86Feature(Feats, I);
@@ -4734,6 +4739,24 @@ void CodeGenModule::emitMultiVersionFunctions() {
47344739
const CodeGenFunction::FMVResolverOption &RHS) {
47354740
return getFMVPriority(TI, LHS).ugt(getFMVPriority(TI, RHS));
47364741
});
4742+
4743+
// Diagnose unreachable function versions.
4744+
if (getTarget().getTriple().isAArch64()) {
4745+
for (auto I = Options.begin() + 1, E = Options.end(); I != E; ++I) {
4746+
llvm::APInt RHS = llvm::AArch64::getCpuSupportsMask(I->Features);
4747+
if (std::any_of(Options.begin(), I, [RHS](auto RO) {
4748+
llvm::APInt LHS = llvm::AArch64::getCpuSupportsMask(RO.Features);
4749+
return LHS.isSubsetOf(RHS);
4750+
})) {
4751+
Diags.Report(DeclMap[I->Function]->getLocation(),
4752+
diag::warn_unreachable_version)
4753+
<< I->Function->getName();
4754+
assert(I->Function->user_empty() && "unexpected users");
4755+
I->Function->eraseFromParent();
4756+
I->Function = nullptr;
4757+
}
4758+
}
4759+
}
47374760
CodeGenFunction CGF(*this);
47384761
CGF.EmitMultiVersionResolver(ResolverFunc, Options);
47394762

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals --include-generated-funcs
2+
// RUN: %clang_cc1 -triple aarch64-linux-gnu -verify -emit-llvm -o - %s | FileCheck %s
3+
4+
__attribute__((target_version("sve;priority=5"))) int unreachable_versions(void) { return 5; }
5+
// expected-warning@+1 {{function version 'unreachable_versions._MlseMmops' is unreachable; ignoring version}}
6+
__attribute__((target_version("mops+lse;priority=1"))) int unreachable_versions(void) { return 1; }
7+
int foo() { return unreachable_versions(); }
8+
// expected-warning@+1 {{function version 'unreachable_versions._Msve2' is unreachable; ignoring version}}
9+
__attribute__((target_clones("sve2;priority=4", "aes+sve2;priority=3", "lse;priority=2", "default"))) int unreachable_versions(void) { return 0; }
10+
// expected-warning@-1 {{function version 'unreachable_versions._MaesMsve2' is unreachable; ignoring version}}
11+
12+
//.
13+
// CHECK: @__aarch64_cpu_features = external dso_local global { i64 }
14+
// CHECK: @unreachable_versions = weak_odr ifunc i32 (), ptr @unreachable_versions.resolver
15+
//.
16+
// CHECK: Function Attrs: noinline nounwind optnone vscale_range(1,16)
17+
// CHECK-LABEL: define {{[^@]+}}@unreachable_versions._Msve
18+
// CHECK-SAME: () #[[ATTR0:[0-9]+]] {
19+
// CHECK-NEXT: entry:
20+
// CHECK-NEXT: ret i32 5
21+
//
22+
//
23+
// CHECK: Function Attrs: noinline nounwind optnone
24+
// CHECK-LABEL: define {{[^@]+}}@foo
25+
// CHECK-SAME: () #[[ATTR1:[0-9]+]] {
26+
// CHECK-NEXT: entry:
27+
// CHECK-NEXT: [[CALL:%.*]] = call i32 @unreachable_versions()
28+
// CHECK-NEXT: ret i32 [[CALL]]
29+
//
30+
//
31+
// CHECK: Function Attrs: noinline nounwind optnone vscale_range(1,16)
32+
// CHECK-LABEL: define {{[^@]+}}@unreachable_versions._Mlse
33+
// CHECK-SAME: () #[[ATTR2:[0-9]+]] {
34+
// CHECK-NEXT: entry:
35+
// CHECK-NEXT: ret i32 0
36+
//
37+
//
38+
// CHECK: Function Attrs: noinline nounwind optnone vscale_range(1,16)
39+
// CHECK-LABEL: define {{[^@]+}}@unreachable_versions.default
40+
// CHECK-SAME: () #[[ATTR3:[0-9]+]] {
41+
// CHECK-NEXT: entry:
42+
// CHECK-NEXT: ret i32 0
43+
//
44+
//
45+
// CHECK: Function Attrs: disable_sanitizer_instrumentation
46+
// CHECK-LABEL: define {{[^@]+}}@unreachable_versions.resolver
47+
// CHECK-SAME: () #[[ATTR4:[0-9]+]] comdat {
48+
// CHECK-NEXT: resolver_entry:
49+
// CHECK-NEXT: call void @__init_cpu_features_resolver()
50+
// CHECK-NEXT: [[TMP0:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
51+
// CHECK-NEXT: [[TMP1:%.*]] = and i64 [[TMP0]], 1073807616
52+
// CHECK-NEXT: [[TMP2:%.*]] = icmp eq i64 [[TMP1]], 1073807616
53+
// CHECK-NEXT: [[TMP3:%.*]] = and i1 true, [[TMP2]]
54+
// CHECK-NEXT: br i1 [[TMP3]], label [[RESOLVER_RETURN:%.*]], label [[RESOLVER_ELSE:%.*]]
55+
// CHECK: resolver_return:
56+
// CHECK-NEXT: ret ptr @unreachable_versions._Msve
57+
// CHECK: resolver_else:
58+
// CHECK-NEXT: [[TMP4:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
59+
// CHECK-NEXT: [[TMP5:%.*]] = and i64 [[TMP4]], 69793284352
60+
// CHECK-NEXT: [[TMP6:%.*]] = icmp eq i64 [[TMP5]], 69793284352
61+
// CHECK-NEXT: [[TMP7:%.*]] = and i1 true, [[TMP6]]
62+
// CHECK-NEXT: [[TMP8:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
63+
// CHECK-NEXT: [[TMP9:%.*]] = and i64 [[TMP8]], 69793317632
64+
// CHECK-NEXT: [[TMP10:%.*]] = icmp eq i64 [[TMP9]], 69793317632
65+
// CHECK-NEXT: [[TMP11:%.*]] = and i1 true, [[TMP10]]
66+
// CHECK-NEXT: [[TMP12:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
67+
// CHECK-NEXT: [[TMP13:%.*]] = and i64 [[TMP12]], 128
68+
// CHECK-NEXT: [[TMP14:%.*]] = icmp eq i64 [[TMP13]], 128
69+
// CHECK-NEXT: [[TMP15:%.*]] = and i1 true, [[TMP14]]
70+
// CHECK-NEXT: br i1 [[TMP15]], label [[RESOLVER_RETURN1:%.*]], label [[RESOLVER_ELSE2:%.*]]
71+
// CHECK: resolver_return1:
72+
// CHECK-NEXT: ret ptr @unreachable_versions._Mlse
73+
// CHECK: resolver_else2:
74+
// CHECK-NEXT: [[TMP16:%.*]] = load i64, ptr @__aarch64_cpu_features, align 8
75+
// CHECK-NEXT: [[TMP17:%.*]] = and i64 [[TMP16]], 576460752303423616
76+
// CHECK-NEXT: [[TMP18:%.*]] = icmp eq i64 [[TMP17]], 576460752303423616
77+
// CHECK-NEXT: [[TMP19:%.*]] = and i1 true, [[TMP18]]
78+
// CHECK-NEXT: ret ptr @unreachable_versions.default
79+
//
80+
//.
81+
// 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" }
82+
// CHECK: attributes #[[ATTR1]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
83+
// 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" }
84+
// CHECK: attributes #[[ATTR3]] = { noinline nounwind optnone vscale_range(1,16) "fmv-features" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
85+
// CHECK: attributes #[[ATTR4]] = { disable_sanitizer_instrumentation }
86+
//.
87+
// CHECK: [[META0:![0-9]+]] = !{i32 1, !"wchar_size", i32 4}
88+
// CHECK: [[META1:![0-9]+]] = !{!"{{.*}}clang version {{.*}}"}
89+
//.

0 commit comments

Comments
 (0)