Skip to content

Commit aaebcbf

Browse files
committed
[llvm][GlobalOpt] Optimize statically resolvable IFuncs (llvm#80606)
1 parent 16df02a commit aaebcbf

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
@@ -90,6 +90,8 @@ STATISTIC(NumAliasesRemoved, "Number of global aliases eliminated");
9090
STATISTIC(NumCXXDtorsRemoved, "Number of global C++ destructors removed");
9191
STATISTIC(NumInternalFunc, "Number of internal functions");
9292
STATISTIC(NumColdCC, "Number of functions marked coldcc");
93+
STATISTIC(NumIFuncsResolved, "Number of statically resolved IFuncs");
94+
STATISTIC(NumIFuncsDeleted, "Number of IFuncs removed");
9395

9496
static cl::opt<bool>
9597
EnableColdCCStressTest("enable-coldcc-stress-test",
@@ -2432,6 +2434,60 @@ static bool OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn) {
24322434
return Changed;
24332435
}
24342436

2437+
static Function *hasSideeffectFreeStaticResolution(GlobalIFunc &IF) {
2438+
if (IF.isInterposable())
2439+
return nullptr;
2440+
2441+
Function *Resolver = IF.getResolverFunction();
2442+
if (!Resolver)
2443+
return nullptr;
2444+
2445+
if (Resolver->isInterposable())
2446+
return nullptr;
2447+
2448+
// Only handle functions that have been optimized into a single basic block.
2449+
auto It = Resolver->begin();
2450+
if (++It != Resolver->end())
2451+
return nullptr;
2452+
2453+
BasicBlock &BB = Resolver->getEntryBlock();
2454+
2455+
if (any_of(BB, [](Instruction &I) { return I.mayHaveSideEffects(); }))
2456+
return nullptr;
2457+
2458+
auto *Ret = dyn_cast<ReturnInst>(BB.getTerminator());
2459+
if (!Ret)
2460+
return nullptr;
2461+
2462+
return dyn_cast<Function>(Ret->getReturnValue());
2463+
}
2464+
2465+
/// Find IFuncs that have resolvers that always point at the same statically
2466+
/// known callee, and replace their callers with a direct call.
2467+
static bool OptimizeStaticIFuncs(Module &M) {
2468+
bool Changed = false;
2469+
for (GlobalIFunc &IF : M.ifuncs())
2470+
if (Function *Callee = hasSideeffectFreeStaticResolution(IF))
2471+
if (!IF.use_empty()) {
2472+
IF.replaceAllUsesWith(Callee);
2473+
NumIFuncsResolved++;
2474+
Changed = true;
2475+
}
2476+
return Changed;
2477+
}
2478+
2479+
static bool
2480+
DeleteDeadIFuncs(Module &M,
2481+
SmallPtrSetImpl<const Comdat *> &NotDiscardableComdats) {
2482+
bool Changed = false;
2483+
for (GlobalIFunc &IF : make_early_inc_range(M.ifuncs()))
2484+
if (deleteIfDead(IF, NotDiscardableComdats)) {
2485+
NumIFuncsDeleted++;
2486+
Changed = true;
2487+
}
2488+
return Changed;
2489+
}
2490+
24352491
static bool
24362492
optimizeGlobalsInModule(Module &M, const DataLayout &DL,
24372493
function_ref<TargetLibraryInfo &(Function &)> GetTLI,
@@ -2492,6 +2548,12 @@ optimizeGlobalsInModule(Module &M, const DataLayout &DL,
24922548
if (CXAAtExitFn)
24932549
LocalChange |= OptimizeEmptyGlobalCXXDtors(CXAAtExitFn);
24942550

2551+
// Optimize IFuncs whose callee's are statically known.
2552+
LocalChange |= OptimizeStaticIFuncs(M);
2553+
2554+
// Remove any IFuncs that are now dead.
2555+
LocalChange |= DeleteDeadIFuncs(M, NotDiscardableComdats);
2556+
24952557
Changed |= LocalChange;
24962558
}
24972559

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)