Skip to content

Commit e976385

Browse files
authored
[llvm][GlobalOpt] Optimize statically resolvable IFuncs (#80606)
1 parent 1b5fae9 commit e976385

File tree

2 files changed

+171
-0
lines changed

2 files changed

+171
-0
lines changed

llvm/lib/Transforms/IPO/GlobalOpt.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ 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");
93+
STATISTIC(NumIFuncsDeleted, "Number of IFuncs removed");
9294

9395
static cl::opt<bool>
9496
EnableColdCCStressTest("enable-coldcc-stress-test",
@@ -2404,6 +2406,60 @@ static bool OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn) {
24042406
return Changed;
24052407
}
24062408

2409+
static Function *hasSideeffectFreeStaticResolution(GlobalIFunc &IF) {
2410+
if (IF.isInterposable())
2411+
return nullptr;
2412+
2413+
Function *Resolver = IF.getResolverFunction();
2414+
if (!Resolver)
2415+
return nullptr;
2416+
2417+
if (Resolver->isInterposable())
2418+
return nullptr;
2419+
2420+
// Only handle functions that have been optimized into a single basic block.
2421+
auto It = Resolver->begin();
2422+
if (++It != Resolver->end())
2423+
return nullptr;
2424+
2425+
BasicBlock &BB = Resolver->getEntryBlock();
2426+
2427+
if (any_of(BB, [](Instruction &I) { return I.mayHaveSideEffects(); }))
2428+
return nullptr;
2429+
2430+
auto *Ret = dyn_cast<ReturnInst>(BB.getTerminator());
2431+
if (!Ret)
2432+
return nullptr;
2433+
2434+
return dyn_cast<Function>(Ret->getReturnValue());
2435+
}
2436+
2437+
/// Find IFuncs that have resolvers that always point at the same statically
2438+
/// known callee, and replace their callers with a direct call.
2439+
static bool OptimizeStaticIFuncs(Module &M) {
2440+
bool Changed = false;
2441+
for (GlobalIFunc &IF : M.ifuncs())
2442+
if (Function *Callee = hasSideeffectFreeStaticResolution(IF))
2443+
if (!IF.use_empty()) {
2444+
IF.replaceAllUsesWith(Callee);
2445+
NumIFuncsResolved++;
2446+
Changed = true;
2447+
}
2448+
return Changed;
2449+
}
2450+
2451+
static bool
2452+
DeleteDeadIFuncs(Module &M,
2453+
SmallPtrSetImpl<const Comdat *> &NotDiscardableComdats) {
2454+
bool Changed = false;
2455+
for (GlobalIFunc &IF : make_early_inc_range(M.ifuncs()))
2456+
if (deleteIfDead(IF, NotDiscardableComdats)) {
2457+
NumIFuncsDeleted++;
2458+
Changed = true;
2459+
}
2460+
return Changed;
2461+
}
2462+
24072463
static bool
24082464
optimizeGlobalsInModule(Module &M, const DataLayout &DL,
24092465
function_ref<TargetLibraryInfo &(Function &)> GetTLI,
@@ -2464,6 +2520,12 @@ optimizeGlobalsInModule(Module &M, const DataLayout &DL,
24642520
if (CXAAtExitFn)
24652521
LocalChange |= OptimizeEmptyGlobalCXXDtors(CXAAtExitFn);
24662522

2523+
// Optimize IFuncs whose callee's are statically known.
2524+
LocalChange |= OptimizeStaticIFuncs(M);
2525+
2526+
// Remove any IFuncs that are now dead.
2527+
LocalChange |= DeleteDeadIFuncs(M, NotDiscardableComdats);
2528+
24672529
Changed |= LocalChange;
24682530
}
24692531

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function caller --check-globals all --version 4
2+
; RUN: opt --passes=globalopt -o - -S < %s | FileCheck %s --implicit-check-not=trivial\.ifunc --implicit-check-not=dead_ifunc
3+
4+
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
5+
target triple = "aarch64-unknown-linux-gnu"
6+
7+
@trivial.ifunc = internal ifunc void (), ptr @trivial.resolver
8+
;.
9+
; CHECK: @unknown_condition = external local_unnamed_addr global i1
10+
; CHECK: @external_ifunc.ifunc = dso_local ifunc void (), ptr @external_ifunc.resolver
11+
; CHECK: @complex.ifunc = internal ifunc void (), ptr @complex.resolver
12+
; CHECK: @sideeffects.ifunc = internal ifunc void (), ptr @sideeffects.resolver
13+
; CHECK: @interposable_ifunc.ifunc = internal ifunc void (), ptr @interposable_ifunc.resolver
14+
; CHECK: @interposable_resolver.ifunc = weak ifunc void (), ptr @interposable_resolver.resolver
15+
;.
16+
define ptr @trivial.resolver() {
17+
ret ptr @trivial._Msimd
18+
}
19+
define void @trivial._Msimd() {
20+
ret void
21+
}
22+
define void @trivial.default() {
23+
ret void
24+
}
25+
26+
27+
@dead_ifunc.ifunc = internal ifunc void (), ptr @trivial.resolver
28+
29+
@external_ifunc.ifunc = dso_local ifunc void (), ptr @external_ifunc.resolver
30+
define ptr @external_ifunc.resolver() {
31+
ret ptr @external_ifunc._Msimd
32+
}
33+
define void @external_ifunc._Msimd() {
34+
ret void
35+
}
36+
define void @external_ifunc.default() {
37+
ret void
38+
}
39+
40+
@unknown_condition = external global i1
41+
@complex.ifunc = internal ifunc void (), ptr @complex.resolver
42+
define ptr @complex.resolver() {
43+
entry:
44+
%v = load i1, ptr @unknown_condition
45+
br i1 %v, label %fast, label %slow
46+
fast:
47+
ret ptr @complex._Msimd
48+
slow:
49+
ret ptr @complex._Msimd
50+
}
51+
define void @complex._Msimd() {
52+
ret void
53+
}
54+
define void @complex.default() {
55+
ret void
56+
}
57+
58+
@sideeffects.ifunc = internal ifunc void (), ptr @sideeffects.resolver
59+
define ptr @sideeffects.resolver() {
60+
store i1 0, ptr @unknown_condition
61+
ret ptr @sideeffects.default
62+
}
63+
define void @sideeffects._Msimd() {
64+
ret void
65+
}
66+
define void @sideeffects.default() {
67+
ret void
68+
}
69+
70+
@interposable_ifunc.ifunc = internal ifunc void (), ptr @interposable_ifunc.resolver
71+
define weak ptr @interposable_ifunc.resolver() {
72+
ret ptr @interposable_ifunc.resolver
73+
}
74+
define void @interposable_ifunc._Msimd() {
75+
ret void
76+
}
77+
define void @interposable_ifunc.default() {
78+
ret void
79+
}
80+
81+
@interposable_resolver.ifunc = weak ifunc void (), ptr @interposable_resolver.resolver
82+
define ptr @interposable_resolver.resolver() {
83+
ret ptr @interposable_resolver.resolver
84+
}
85+
define void @interposable_resolver._Msimd() {
86+
ret void
87+
}
88+
define void @interposable_resolver.default() {
89+
ret void
90+
}
91+
92+
define void @caller() {
93+
; CHECK-LABEL: define void @caller() local_unnamed_addr {
94+
; CHECK-NEXT: call void @trivial._Msimd()
95+
; CHECK-NEXT: call void @external_ifunc._Msimd()
96+
; CHECK-NEXT: call void @complex.ifunc()
97+
; CHECK-NEXT: call void @sideeffects.ifunc()
98+
; CHECK-NEXT: call void @interposable_ifunc.ifunc()
99+
; CHECK-NEXT: call void @interposable_resolver.ifunc()
100+
; CHECK-NEXT: ret void
101+
;
102+
call void @trivial.ifunc()
103+
call void @external_ifunc.ifunc()
104+
call void @complex.ifunc()
105+
call void @sideeffects.ifunc()
106+
call void @interposable_ifunc.ifunc()
107+
call void @interposable_resolver.ifunc()
108+
ret void
109+
}

0 commit comments

Comments
 (0)