Skip to content

Commit 5318a4d

Browse files
committed
[llvm][GlobalOpt] Optimize statically resolvable IFuncs
1 parent 659419a commit 5318a4d

File tree

2 files changed

+121
-0
lines changed

2 files changed

+121
-0
lines changed

llvm/lib/Transforms/IPO/GlobalOpt.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ STATISTIC(NumAliasesRemoved, "Number of global aliases eliminated");
8989
STATISTIC(NumCXXDtorsRemoved, "Number of global C++ destructors removed");
9090
STATISTIC(NumInternalFunc, "Number of internal functions");
9191
STATISTIC(NumColdCC, "Number of functions marked coldcc");
92+
STATISTIC(NumIFuncsResolved, "Number of statically resolved IFuncs");
9293

9394
static cl::opt<bool>
9495
EnableColdCCStressTest("enable-coldcc-stress-test",
@@ -2404,6 +2405,42 @@ static bool OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn) {
24042405
return Changed;
24052406
}
24062407

2408+
static Function *hasSideeffectFreeStaticResolution(GlobalIFunc &IF) {
2409+
Function *Resolver = IF.getResolverFunction();
2410+
if (!Resolver)
2411+
return nullptr;
2412+
2413+
Function *Callee = nullptr;
2414+
for (BasicBlock &BB : *Resolver) {
2415+
if (any_of(BB, [](Instruction &I) { return I.mayHaveSideEffects(); }))
2416+
return nullptr;
2417+
2418+
if (auto *Ret = dyn_cast<ReturnInst>(BB.getTerminator()))
2419+
if (auto *F = dyn_cast<Function>(Ret->getReturnValue())) {
2420+
if (!Callee)
2421+
Callee = F;
2422+
else if (F != Callee)
2423+
return nullptr;
2424+
}
2425+
}
2426+
2427+
return Callee;
2428+
}
2429+
2430+
/// Find IFuncs that have resolvers that always point at the same statically
2431+
/// known callee, and replace their callers with a direct call.
2432+
static bool OptimizeStaticIFuncs(Module &M) {
2433+
bool Changed = false;
2434+
for (GlobalIFunc &IF : M.ifuncs())
2435+
if (Function *Callee = hasSideeffectFreeStaticResolution(IF))
2436+
if (!IF.use_empty()) {
2437+
IF.replaceAllUsesWith(Callee);
2438+
NumIFuncsResolved++;
2439+
Changed = true;
2440+
}
2441+
return Changed;
2442+
}
2443+
24072444
static bool
24082445
optimizeGlobalsInModule(Module &M, const DataLayout &DL,
24092446
function_ref<TargetLibraryInfo &(Function &)> GetTLI,
@@ -2464,6 +2501,9 @@ optimizeGlobalsInModule(Module &M, const DataLayout &DL,
24642501
if (CXAAtExitFn)
24652502
LocalChange |= OptimizeEmptyGlobalCXXDtors(CXAAtExitFn);
24662503

2504+
// Optimize IFuncs whose callee's are statically known.
2505+
LocalChange |= OptimizeStaticIFuncs(M);
2506+
24672507
Changed |= LocalChange;
24682508
}
24692509

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
; RUN: opt --passes=globalopt -o - -S < %s | FileCheck %s
2+
3+
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
4+
target triple = "aarch64-unknown-linux-gnu"
5+
6+
$callee_with_trivial_resolver.resolver = comdat any
7+
@callee_with_trivial_resolver.ifunc = weak_odr dso_local ifunc void (), ptr @callee_with_trivial_resolver.resolver
8+
define weak_odr ptr @callee_with_trivial_resolver.resolver() comdat {
9+
ret ptr @callee_with_trivial_resolver._Msimd
10+
}
11+
define void @callee_with_trivial_resolver._Msimd() {
12+
ret void
13+
}
14+
define void @callee_with_trivial_resolver.default() {
15+
ret void
16+
}
17+
18+
@unknown_condition = external global i1
19+
$callee_with_complex_static_resolver.resolver = comdat any
20+
@callee_with_complex_static_resolver.ifunc = weak_odr dso_local ifunc void (), ptr @callee_with_complex_static_resolver.resolver
21+
define weak_odr ptr @callee_with_complex_static_resolver.resolver() comdat {
22+
entry:
23+
%v = load i1, ptr @unknown_condition
24+
br i1 %v, label %fast, label %slow
25+
fast:
26+
ret ptr @callee_with_complex_static_resolver._Msimd
27+
slow:
28+
ret ptr @callee_with_complex_static_resolver._Msimd
29+
}
30+
define void @callee_with_complex_static_resolver._Msimd() {
31+
ret void
32+
}
33+
define void @callee_with_complex_static_resolver.default() {
34+
ret void
35+
}
36+
37+
$callee_with_complex_dynamic_resolver.resolver = comdat any
38+
@callee_with_complex_dynamic_resolver.ifunc = weak_odr dso_local ifunc void (), ptr @callee_with_complex_dynamic_resolver.resolver
39+
define weak_odr ptr @callee_with_complex_dynamic_resolver.resolver() comdat {
40+
entry:
41+
%v = load i1, ptr @unknown_condition
42+
br i1 %v, label %fast, label %slow
43+
fast:
44+
ret ptr @callee_with_complex_dynamic_resolver._Msimd
45+
slow:
46+
ret ptr @callee_with_complex_dynamic_resolver.default
47+
}
48+
define void @callee_with_complex_dynamic_resolver._Msimd() {
49+
ret void
50+
}
51+
define void @callee_with_complex_dynamic_resolver.default() {
52+
ret void
53+
}
54+
55+
$callee_with_sideeffects_resolver.resolver = comdat any
56+
@callee_with_sideeffects_resolver.ifunc = weak_odr dso_local ifunc void (), ptr @callee_with_sideeffects_resolver.resolver
57+
define weak_odr ptr @callee_with_sideeffects_resolver.resolver() comdat {
58+
store i1 0, ptr @unknown_condition
59+
ret ptr @callee_with_sideeffects_resolver.default
60+
}
61+
define void @callee_with_sideeffects_resolver._Msimd() {
62+
ret void
63+
}
64+
define void @callee_with_sideeffects_resolver.default() {
65+
ret void
66+
}
67+
68+
define void @caller() {
69+
call void @callee_with_trivial_resolver.ifunc()
70+
call void @callee_with_complex_static_resolver.ifunc()
71+
call void @callee_with_complex_dynamic_resolver.ifunc()
72+
call void @callee_with_sideeffects_resolver.ifunc()
73+
ret void
74+
}
75+
76+
; CHECK-LABEL: define void @caller()
77+
; CHECK-NEXT: call void @callee_with_trivial_resolver._Msimd()
78+
; CHECK-NEXT: call void @callee_with_complex_static_resolver._Msimd()
79+
; CHECK-NEXT: call void @callee_with_complex_dynamic_resolver.ifunc()
80+
; CHECK-NEXT: call void @callee_with_sideeffects_resolver.ifunc()
81+
; CHECK-NEXT: ret void

0 commit comments

Comments
 (0)