[llvm] [llvm][GlobalOpt] Optimize statically resolvable IFuncs (PR #80606)
Jon Roelofs via llvm-commits
llvm-commits at lists.llvm.org
Tue Feb 6 09:07:30 PST 2024
https://github.com/jroelofs updated https://github.com/llvm/llvm-project/pull/80606
>From 5318a4dd324a3e74130c7bd9f0b0031e1740d847 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <jonathan_roelofs at apple.com>
Date: Sun, 4 Feb 2024 08:41:11 -0800
Subject: [PATCH 01/10] [llvm][GlobalOpt] Optimize statically resolvable IFuncs
---
llvm/lib/Transforms/IPO/GlobalOpt.cpp | 40 +++++++++
.../GlobalOpt/resolve-static-ifunc.ll | 81 +++++++++++++++++++
2 files changed, 121 insertions(+)
create mode 100644 llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll
diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index 951372adcfa93..32f23ffcefb70 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -89,6 +89,7 @@ STATISTIC(NumAliasesRemoved, "Number of global aliases eliminated");
STATISTIC(NumCXXDtorsRemoved, "Number of global C++ destructors removed");
STATISTIC(NumInternalFunc, "Number of internal functions");
STATISTIC(NumColdCC, "Number of functions marked coldcc");
+STATISTIC(NumIFuncsResolved, "Number of statically resolved IFuncs");
static cl::opt<bool>
EnableColdCCStressTest("enable-coldcc-stress-test",
@@ -2404,6 +2405,42 @@ static bool OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn) {
return Changed;
}
+static Function *hasSideeffectFreeStaticResolution(GlobalIFunc &IF) {
+ Function *Resolver = IF.getResolverFunction();
+ if (!Resolver)
+ return nullptr;
+
+ Function *Callee = nullptr;
+ for (BasicBlock &BB : *Resolver) {
+ if (any_of(BB, [](Instruction &I) { return I.mayHaveSideEffects(); }))
+ return nullptr;
+
+ if (auto *Ret = dyn_cast<ReturnInst>(BB.getTerminator()))
+ if (auto *F = dyn_cast<Function>(Ret->getReturnValue())) {
+ if (!Callee)
+ Callee = F;
+ else if (F != Callee)
+ return nullptr;
+ }
+ }
+
+ return Callee;
+}
+
+/// Find IFuncs that have resolvers that always point at the same statically
+/// known callee, and replace their callers with a direct call.
+static bool OptimizeStaticIFuncs(Module &M) {
+ bool Changed = false;
+ for (GlobalIFunc &IF : M.ifuncs())
+ if (Function *Callee = hasSideeffectFreeStaticResolution(IF))
+ if (!IF.use_empty()) {
+ IF.replaceAllUsesWith(Callee);
+ NumIFuncsResolved++;
+ Changed = true;
+ }
+ return Changed;
+}
+
static bool
optimizeGlobalsInModule(Module &M, const DataLayout &DL,
function_ref<TargetLibraryInfo &(Function &)> GetTLI,
@@ -2464,6 +2501,9 @@ optimizeGlobalsInModule(Module &M, const DataLayout &DL,
if (CXAAtExitFn)
LocalChange |= OptimizeEmptyGlobalCXXDtors(CXAAtExitFn);
+ // Optimize IFuncs whose callee's are statically known.
+ LocalChange |= OptimizeStaticIFuncs(M);
+
Changed |= LocalChange;
}
diff --git a/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll b/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll
new file mode 100644
index 0000000000000..d51e9111ad2cd
--- /dev/null
+++ b/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll
@@ -0,0 +1,81 @@
+; RUN: opt --passes=globalopt -o - -S < %s | FileCheck %s
+
+target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
+target triple = "aarch64-unknown-linux-gnu"
+
+$callee_with_trivial_resolver.resolver = comdat any
+ at callee_with_trivial_resolver.ifunc = weak_odr dso_local ifunc void (), ptr @callee_with_trivial_resolver.resolver
+define weak_odr ptr @callee_with_trivial_resolver.resolver() comdat {
+ ret ptr @callee_with_trivial_resolver._Msimd
+}
+define void @callee_with_trivial_resolver._Msimd() {
+ ret void
+}
+define void @callee_with_trivial_resolver.default() {
+ ret void
+}
+
+ at unknown_condition = external global i1
+$callee_with_complex_static_resolver.resolver = comdat any
+ at callee_with_complex_static_resolver.ifunc = weak_odr dso_local ifunc void (), ptr @callee_with_complex_static_resolver.resolver
+define weak_odr ptr @callee_with_complex_static_resolver.resolver() comdat {
+entry:
+ %v = load i1, ptr @unknown_condition
+ br i1 %v, label %fast, label %slow
+fast:
+ ret ptr @callee_with_complex_static_resolver._Msimd
+slow:
+ ret ptr @callee_with_complex_static_resolver._Msimd
+}
+define void @callee_with_complex_static_resolver._Msimd() {
+ ret void
+}
+define void @callee_with_complex_static_resolver.default() {
+ ret void
+}
+
+$callee_with_complex_dynamic_resolver.resolver = comdat any
+ at callee_with_complex_dynamic_resolver.ifunc = weak_odr dso_local ifunc void (), ptr @callee_with_complex_dynamic_resolver.resolver
+define weak_odr ptr @callee_with_complex_dynamic_resolver.resolver() comdat {
+entry:
+ %v = load i1, ptr @unknown_condition
+ br i1 %v, label %fast, label %slow
+fast:
+ ret ptr @callee_with_complex_dynamic_resolver._Msimd
+slow:
+ ret ptr @callee_with_complex_dynamic_resolver.default
+}
+define void @callee_with_complex_dynamic_resolver._Msimd() {
+ ret void
+}
+define void @callee_with_complex_dynamic_resolver.default() {
+ ret void
+}
+
+$callee_with_sideeffects_resolver.resolver = comdat any
+ at callee_with_sideeffects_resolver.ifunc = weak_odr dso_local ifunc void (), ptr @callee_with_sideeffects_resolver.resolver
+define weak_odr ptr @callee_with_sideeffects_resolver.resolver() comdat {
+ store i1 0, ptr @unknown_condition
+ ret ptr @callee_with_sideeffects_resolver.default
+}
+define void @callee_with_sideeffects_resolver._Msimd() {
+ ret void
+}
+define void @callee_with_sideeffects_resolver.default() {
+ ret void
+}
+
+define void @caller() {
+ call void @callee_with_trivial_resolver.ifunc()
+ call void @callee_with_complex_static_resolver.ifunc()
+ call void @callee_with_complex_dynamic_resolver.ifunc()
+ call void @callee_with_sideeffects_resolver.ifunc()
+ ret void
+}
+
+; CHECK-LABEL: define void @caller()
+; CHECK-NEXT: call void @callee_with_trivial_resolver._Msimd()
+; CHECK-NEXT: call void @callee_with_complex_static_resolver._Msimd()
+; CHECK-NEXT: call void @callee_with_complex_dynamic_resolver.ifunc()
+; CHECK-NEXT: call void @callee_with_sideeffects_resolver.ifunc()
+; CHECK-NEXT: ret void
>From 633116c658f5d0202088e882f100265e3fba6f0d Mon Sep 17 00:00:00 2001
From: Jon Roelofs <jonathan_roelofs at apple.com>
Date: Mon, 5 Feb 2024 09:19:57 -0800
Subject: [PATCH 02/10] apply nikic's feedback
---
llvm/lib/Transforms/IPO/GlobalOpt.cpp | 29 ++++----
.../GlobalOpt/resolve-static-ifunc.ll | 67 ++++++++-----------
2 files changed, 45 insertions(+), 51 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index 32f23ffcefb70..3786ed1fec454 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -2410,21 +2410,24 @@ static Function *hasSideeffectFreeStaticResolution(GlobalIFunc &IF) {
if (!Resolver)
return nullptr;
- Function *Callee = nullptr;
- for (BasicBlock &BB : *Resolver) {
- if (any_of(BB, [](Instruction &I) { return I.mayHaveSideEffects(); }))
- return nullptr;
+ if (Resolver->isInterposable())
+ return nullptr;
- if (auto *Ret = dyn_cast<ReturnInst>(BB.getTerminator()))
- if (auto *F = dyn_cast<Function>(Ret->getReturnValue())) {
- if (!Callee)
- Callee = F;
- else if (F != Callee)
- return nullptr;
- }
- }
+ // Only handle functions that have been optimized into a single basic block.
+ auto It = Resolver->begin();
+ if (++It != Resolver->end())
+ return nullptr;
+
+ BasicBlock &BB = Resolver->getEntryBlock();
+
+ if (any_of(BB, [](Instruction &I) { return I.mayHaveSideEffects(); }))
+ return nullptr;
+
+ auto *Ret = dyn_cast<ReturnInst>(BB.getTerminator());
+ if (!Ret)
+ return nullptr;
- return Callee;
+ return dyn_cast<Function>(Ret->getReturnValue());
}
/// Find IFuncs that have resolvers that always point at the same statically
diff --git a/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll b/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll
index d51e9111ad2cd..3069930f266b4 100644
--- a/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll
+++ b/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll
@@ -1,11 +1,11 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function caller --version 4
; RUN: opt --passes=globalopt -o - -S < %s | FileCheck %s
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64-unknown-linux-gnu"
-$callee_with_trivial_resolver.resolver = comdat any
- at callee_with_trivial_resolver.ifunc = weak_odr dso_local ifunc void (), ptr @callee_with_trivial_resolver.resolver
-define weak_odr ptr @callee_with_trivial_resolver.resolver() comdat {
+ at callee_with_trivial_resolver.ifunc = dso_local ifunc void (), ptr @callee_with_trivial_resolver.resolver
+define ptr @callee_with_trivial_resolver.resolver() {
ret ptr @callee_with_trivial_resolver._Msimd
}
define void @callee_with_trivial_resolver._Msimd() {
@@ -16,66 +16,57 @@ define void @callee_with_trivial_resolver.default() {
}
@unknown_condition = external global i1
-$callee_with_complex_static_resolver.resolver = comdat any
- at callee_with_complex_static_resolver.ifunc = weak_odr dso_local ifunc void (), ptr @callee_with_complex_static_resolver.resolver
-define weak_odr ptr @callee_with_complex_static_resolver.resolver() comdat {
+ at callee_with_complex_resolver.ifunc = dso_local ifunc void (), ptr @callee_with_complex_resolver.resolver
+define ptr @callee_with_complex_resolver.resolver() {
entry:
%v = load i1, ptr @unknown_condition
br i1 %v, label %fast, label %slow
fast:
- ret ptr @callee_with_complex_static_resolver._Msimd
+ ret ptr @callee_with_complex_resolver._Msimd
slow:
- ret ptr @callee_with_complex_static_resolver._Msimd
+ ret ptr @callee_with_complex_resolver._Msimd
}
-define void @callee_with_complex_static_resolver._Msimd() {
+define void @callee_with_complex_resolver._Msimd() {
ret void
}
-define void @callee_with_complex_static_resolver.default() {
+define void @callee_with_complex_resolver.default() {
ret void
}
-$callee_with_complex_dynamic_resolver.resolver = comdat any
- at callee_with_complex_dynamic_resolver.ifunc = weak_odr dso_local ifunc void (), ptr @callee_with_complex_dynamic_resolver.resolver
-define weak_odr ptr @callee_with_complex_dynamic_resolver.resolver() comdat {
-entry:
- %v = load i1, ptr @unknown_condition
- br i1 %v, label %fast, label %slow
-fast:
- ret ptr @callee_with_complex_dynamic_resolver._Msimd
-slow:
- ret ptr @callee_with_complex_dynamic_resolver.default
+ at callee_with_sideeffects_resolver.ifunc = dso_local ifunc void (), ptr @callee_with_sideeffects_resolver.resolver
+define ptr @callee_with_sideeffects_resolver.resolver() {
+ store i1 0, ptr @unknown_condition
+ ret ptr @callee_with_sideeffects_resolver.default
}
-define void @callee_with_complex_dynamic_resolver._Msimd() {
+define void @callee_with_sideeffects_resolver._Msimd() {
ret void
}
-define void @callee_with_complex_dynamic_resolver.default() {
+define void @callee_with_sideeffects_resolver.default() {
ret void
}
-$callee_with_sideeffects_resolver.resolver = comdat any
- at callee_with_sideeffects_resolver.ifunc = weak_odr dso_local ifunc void (), ptr @callee_with_sideeffects_resolver.resolver
-define weak_odr ptr @callee_with_sideeffects_resolver.resolver() comdat {
- store i1 0, ptr @unknown_condition
- ret ptr @callee_with_sideeffects_resolver.default
+ at callee_with_interposable_resolver.ifunc = dso_local ifunc void (), ptr @callee_with_interposable_resolver.resolver
+define weak ptr @callee_with_interposable_resolver.resolver() {
+ ret ptr @callee_with_interposable_resolver.resolver
}
-define void @callee_with_sideeffects_resolver._Msimd() {
+define void @callee_with_interposable_resolver._Msimd() {
ret void
}
-define void @callee_with_sideeffects_resolver.default() {
+define void @callee_with_interposable_resolver.default() {
ret void
}
define void @caller() {
+; CHECK-LABEL: define void @caller() local_unnamed_addr {
+; CHECK-NEXT: call void @callee_with_trivial_resolver._Msimd()
+; CHECK-NEXT: call void @callee_with_complex_resolver.ifunc()
+; CHECK-NEXT: call void @callee_with_sideeffects_resolver.ifunc()
+; CHECK-NEXT: call void @callee_with_interposable_resolver.ifunc()
+; CHECK-NEXT: ret void
+;
call void @callee_with_trivial_resolver.ifunc()
- call void @callee_with_complex_static_resolver.ifunc()
- call void @callee_with_complex_dynamic_resolver.ifunc()
+ call void @callee_with_complex_resolver.ifunc()
call void @callee_with_sideeffects_resolver.ifunc()
+ call void @callee_with_interposable_resolver.ifunc()
ret void
}
-
-; CHECK-LABEL: define void @caller()
-; CHECK-NEXT: call void @callee_with_trivial_resolver._Msimd()
-; CHECK-NEXT: call void @callee_with_complex_static_resolver._Msimd()
-; CHECK-NEXT: call void @callee_with_complex_dynamic_resolver.ifunc()
-; CHECK-NEXT: call void @callee_with_sideeffects_resolver.ifunc()
-; CHECK-NEXT: ret void
>From a62ad245598aac9257705f7fcf6e541ccc561542 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <jonathan_roelofs at apple.com>
Date: Mon, 5 Feb 2024 09:34:40 -0800
Subject: [PATCH 03/10] delete dead ifuncs
---
llvm/lib/Transforms/IPO/GlobalOpt.cpp | 13 ++++++++
.../GlobalOpt/resolve-static-ifunc.ll | 32 +++++++++++++++----
2 files changed, 39 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index 3786ed1fec454..a77674084a46b 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -90,6 +90,7 @@ STATISTIC(NumCXXDtorsRemoved, "Number of global C++ destructors removed");
STATISTIC(NumInternalFunc, "Number of internal functions");
STATISTIC(NumColdCC, "Number of functions marked coldcc");
STATISTIC(NumIFuncsResolved, "Number of statically resolved IFuncs");
+STATISTIC(NumIFuncsDeleted, "Number of IFuncs removed");
static cl::opt<bool>
EnableColdCCStressTest("enable-coldcc-stress-test",
@@ -2444,6 +2445,16 @@ static bool OptimizeStaticIFuncs(Module &M) {
return Changed;
}
+static bool DeleteDeadIFuncs(Module &M) {
+ bool Changed = false;
+ for (auto I = M.ifunc_begin(), E = M.ifunc_end(); I != E; ++I)
+ if (I->use_empty() && I->isDiscardableIfUnused()) {
+ (&*I++)->eraseFromParent();
+ NumIFuncsDeleted++;
+ }
+ return Changed;
+}
+
static bool
optimizeGlobalsInModule(Module &M, const DataLayout &DL,
function_ref<TargetLibraryInfo &(Function &)> GetTLI,
@@ -2507,6 +2518,8 @@ optimizeGlobalsInModule(Module &M, const DataLayout &DL,
// Optimize IFuncs whose callee's are statically known.
LocalChange |= OptimizeStaticIFuncs(M);
+ LocalChange |= DeleteDeadIFuncs(M);
+
Changed |= LocalChange;
}
diff --git a/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll b/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll
index 3069930f266b4..be036a7ff61a2 100644
--- a/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll
+++ b/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll
@@ -1,10 +1,17 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function caller --version 4
-; RUN: opt --passes=globalopt -o - -S < %s | FileCheck %s
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function caller --check-globals all --version 4
+; RUN: opt --passes=globalopt -o - -S < %s | FileCheck %s --implicit-check-not=callee_with_trivial_resolver\.ifunc
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64-unknown-linux-gnu"
- at callee_with_trivial_resolver.ifunc = dso_local ifunc void (), ptr @callee_with_trivial_resolver.resolver
+ at callee_with_trivial_resolver.ifunc = internal ifunc void (), ptr @callee_with_trivial_resolver.resolver
+;.
+; CHECK: @unknown_condition = external local_unnamed_addr global i1
+; CHECK: @callee_with_external_ifunc.ifunc = dso_local ifunc void (), ptr @callee_with_trivial_resolver.resolver
+; CHECK: @callee_with_complex_resolver.ifunc = internal ifunc void (), ptr @callee_with_complex_resolver.resolver
+; CHECK: @callee_with_sideeffects_resolver.ifunc = internal ifunc void (), ptr @callee_with_sideeffects_resolver.resolver
+; CHECK: @callee_with_interposable_resolver.ifunc = internal ifunc void (), ptr @callee_with_interposable_resolver.resolver
+;.
define ptr @callee_with_trivial_resolver.resolver() {
ret ptr @callee_with_trivial_resolver._Msimd
}
@@ -15,8 +22,19 @@ define void @callee_with_trivial_resolver.default() {
ret void
}
+ at callee_with_external_ifunc.ifunc = dso_local ifunc void (), ptr @callee_with_trivial_resolver.resolver
+define ptr @callee_with_external_ifunc.resolver() {
+ ret ptr @callee_with_external_ifunc._Msimd
+}
+define void @callee_with_external_ifunc._Msimd() {
+ ret void
+}
+define void @callee_with_external_ifunc.default() {
+ ret void
+}
+
@unknown_condition = external global i1
- at callee_with_complex_resolver.ifunc = dso_local ifunc void (), ptr @callee_with_complex_resolver.resolver
+ at callee_with_complex_resolver.ifunc = internal ifunc void (), ptr @callee_with_complex_resolver.resolver
define ptr @callee_with_complex_resolver.resolver() {
entry:
%v = load i1, ptr @unknown_condition
@@ -33,7 +51,7 @@ define void @callee_with_complex_resolver.default() {
ret void
}
- at callee_with_sideeffects_resolver.ifunc = dso_local ifunc void (), ptr @callee_with_sideeffects_resolver.resolver
+ at callee_with_sideeffects_resolver.ifunc = internal ifunc void (), ptr @callee_with_sideeffects_resolver.resolver
define ptr @callee_with_sideeffects_resolver.resolver() {
store i1 0, ptr @unknown_condition
ret ptr @callee_with_sideeffects_resolver.default
@@ -45,7 +63,7 @@ define void @callee_with_sideeffects_resolver.default() {
ret void
}
- at callee_with_interposable_resolver.ifunc = dso_local ifunc void (), ptr @callee_with_interposable_resolver.resolver
+ at callee_with_interposable_resolver.ifunc = internal ifunc void (), ptr @callee_with_interposable_resolver.resolver
define weak ptr @callee_with_interposable_resolver.resolver() {
ret ptr @callee_with_interposable_resolver.resolver
}
@@ -59,12 +77,14 @@ define void @callee_with_interposable_resolver.default() {
define void @caller() {
; CHECK-LABEL: define void @caller() local_unnamed_addr {
; CHECK-NEXT: call void @callee_with_trivial_resolver._Msimd()
+; CHECK-NEXT: call void @callee_with_trivial_resolver._Msimd()
; CHECK-NEXT: call void @callee_with_complex_resolver.ifunc()
; CHECK-NEXT: call void @callee_with_sideeffects_resolver.ifunc()
; CHECK-NEXT: call void @callee_with_interposable_resolver.ifunc()
; CHECK-NEXT: ret void
;
call void @callee_with_trivial_resolver.ifunc()
+ call void @callee_with_external_ifunc.ifunc()
call void @callee_with_complex_resolver.ifunc()
call void @callee_with_sideeffects_resolver.ifunc()
call void @callee_with_interposable_resolver.ifunc()
>From 940090f5ab562dfed938b47b26f233bcf65b4240 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <jonathan_roelofs at apple.com>
Date: Mon, 5 Feb 2024 09:47:29 -0800
Subject: [PATCH 04/10] simplify fn names in test
---
.../GlobalOpt/resolve-static-ifunc.ll | 82 +++++++++----------
1 file changed, 41 insertions(+), 41 deletions(-)
diff --git a/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll b/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll
index be036a7ff61a2..267afa6e03114 100644
--- a/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll
+++ b/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll
@@ -1,92 +1,92 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function caller --check-globals all --version 4
-; RUN: opt --passes=globalopt -o - -S < %s | FileCheck %s --implicit-check-not=callee_with_trivial_resolver\.ifunc
+; RUN: opt --passes=globalopt -o - -S < %s | FileCheck %s --implicit-check-not=trivial\.ifunc
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64-unknown-linux-gnu"
- at callee_with_trivial_resolver.ifunc = internal ifunc void (), ptr @callee_with_trivial_resolver.resolver
+ at trivial.ifunc = internal ifunc void (), ptr @trivial.resolver
;.
; CHECK: @unknown_condition = external local_unnamed_addr global i1
-; CHECK: @callee_with_external_ifunc.ifunc = dso_local ifunc void (), ptr @callee_with_trivial_resolver.resolver
-; CHECK: @callee_with_complex_resolver.ifunc = internal ifunc void (), ptr @callee_with_complex_resolver.resolver
-; CHECK: @callee_with_sideeffects_resolver.ifunc = internal ifunc void (), ptr @callee_with_sideeffects_resolver.resolver
-; CHECK: @callee_with_interposable_resolver.ifunc = internal ifunc void (), ptr @callee_with_interposable_resolver.resolver
+; CHECK: @external_ifunc.ifunc = dso_local ifunc void (), ptr @trivial.resolver
+; CHECK: @complex.ifunc = internal ifunc void (), ptr @complex.resolver
+; CHECK: @sideeffects.ifunc = internal ifunc void (), ptr @sideeffects.resolver
+; CHECK: @interposable.ifunc = internal ifunc void (), ptr @interposable.resolver
;.
-define ptr @callee_with_trivial_resolver.resolver() {
- ret ptr @callee_with_trivial_resolver._Msimd
+define ptr @trivial.resolver() {
+ ret ptr @trivial._Msimd
}
-define void @callee_with_trivial_resolver._Msimd() {
+define void @trivial._Msimd() {
ret void
}
-define void @callee_with_trivial_resolver.default() {
+define void @trivial.default() {
ret void
}
- at callee_with_external_ifunc.ifunc = dso_local ifunc void (), ptr @callee_with_trivial_resolver.resolver
-define ptr @callee_with_external_ifunc.resolver() {
- ret ptr @callee_with_external_ifunc._Msimd
+ at external_ifunc.ifunc = dso_local ifunc void (), ptr @trivial.resolver
+define ptr @external_ifunc.resolver() {
+ ret ptr @external_ifunc._Msimd
}
-define void @callee_with_external_ifunc._Msimd() {
+define void @external_ifunc._Msimd() {
ret void
}
-define void @callee_with_external_ifunc.default() {
+define void @external_ifunc.default() {
ret void
}
@unknown_condition = external global i1
- at callee_with_complex_resolver.ifunc = internal ifunc void (), ptr @callee_with_complex_resolver.resolver
-define ptr @callee_with_complex_resolver.resolver() {
+ at complex.ifunc = internal ifunc void (), ptr @complex.resolver
+define ptr @complex.resolver() {
entry:
%v = load i1, ptr @unknown_condition
br i1 %v, label %fast, label %slow
fast:
- ret ptr @callee_with_complex_resolver._Msimd
+ ret ptr @complex._Msimd
slow:
- ret ptr @callee_with_complex_resolver._Msimd
+ ret ptr @complex._Msimd
}
-define void @callee_with_complex_resolver._Msimd() {
+define void @complex._Msimd() {
ret void
}
-define void @callee_with_complex_resolver.default() {
+define void @complex.default() {
ret void
}
- at callee_with_sideeffects_resolver.ifunc = internal ifunc void (), ptr @callee_with_sideeffects_resolver.resolver
-define ptr @callee_with_sideeffects_resolver.resolver() {
+ at sideeffects.ifunc = internal ifunc void (), ptr @sideeffects.resolver
+define ptr @sideeffects.resolver() {
store i1 0, ptr @unknown_condition
- ret ptr @callee_with_sideeffects_resolver.default
+ ret ptr @sideeffects.default
}
-define void @callee_with_sideeffects_resolver._Msimd() {
+define void @sideeffects._Msimd() {
ret void
}
-define void @callee_with_sideeffects_resolver.default() {
+define void @sideeffects.default() {
ret void
}
- at callee_with_interposable_resolver.ifunc = internal ifunc void (), ptr @callee_with_interposable_resolver.resolver
-define weak ptr @callee_with_interposable_resolver.resolver() {
- ret ptr @callee_with_interposable_resolver.resolver
+ at interposable.ifunc = internal ifunc void (), ptr @interposable.resolver
+define weak ptr @interposable.resolver() {
+ ret ptr @interposable.resolver
}
-define void @callee_with_interposable_resolver._Msimd() {
+define void @interposable._Msimd() {
ret void
}
-define void @callee_with_interposable_resolver.default() {
+define void @interposable.default() {
ret void
}
define void @caller() {
; CHECK-LABEL: define void @caller() local_unnamed_addr {
-; CHECK-NEXT: call void @callee_with_trivial_resolver._Msimd()
-; CHECK-NEXT: call void @callee_with_trivial_resolver._Msimd()
-; CHECK-NEXT: call void @callee_with_complex_resolver.ifunc()
-; CHECK-NEXT: call void @callee_with_sideeffects_resolver.ifunc()
-; CHECK-NEXT: call void @callee_with_interposable_resolver.ifunc()
+; CHECK-NEXT: call void @trivial._Msimd()
+; CHECK-NEXT: call void @trivial._Msimd()
+; CHECK-NEXT: call void @complex.ifunc()
+; CHECK-NEXT: call void @sideeffects.ifunc()
+; CHECK-NEXT: call void @interposable.ifunc()
; CHECK-NEXT: ret void
;
- call void @callee_with_trivial_resolver.ifunc()
- call void @callee_with_external_ifunc.ifunc()
- call void @callee_with_complex_resolver.ifunc()
- call void @callee_with_sideeffects_resolver.ifunc()
- call void @callee_with_interposable_resolver.ifunc()
+ call void @trivial.ifunc()
+ call void @external_ifunc.ifunc()
+ call void @complex.ifunc()
+ call void @sideeffects.ifunc()
+ call void @interposable.ifunc()
ret void
}
>From c05c28bba1b21012aa40cb8b03883a726cdd7466 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <jonathan_roelofs at apple.com>
Date: Mon, 5 Feb 2024 09:48:01 -0800
Subject: [PATCH 05/10] don't forget to update Changed
---
llvm/lib/Transforms/IPO/GlobalOpt.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index a77674084a46b..d75d913cfda2b 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -2451,6 +2451,7 @@ static bool DeleteDeadIFuncs(Module &M) {
if (I->use_empty() && I->isDiscardableIfUnused()) {
(&*I++)->eraseFromParent();
NumIFuncsDeleted++;
+ Changed = true;
}
return Changed;
}
>From 929c23ec61f5805f8c3ff11d8ea4736ce74df241 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <jonathan_roelofs at apple.com>
Date: Mon, 5 Feb 2024 10:00:30 -0800
Subject: [PATCH 06/10] use deleteIfDead
---
llvm/lib/Transforms/IPO/GlobalOpt.cpp | 10 +++++-----
llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll | 5 ++++-
2 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index d75d913cfda2b..95a4a1f4f7b07 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -2445,11 +2445,10 @@ static bool OptimizeStaticIFuncs(Module &M) {
return Changed;
}
-static bool DeleteDeadIFuncs(Module &M) {
+static bool DeleteDeadIFuncs(Module &M, SmallPtrSetImpl<const Comdat *> &NotDiscardableComdats) {
bool Changed = false;
- for (auto I = M.ifunc_begin(), E = M.ifunc_end(); I != E; ++I)
- if (I->use_empty() && I->isDiscardableIfUnused()) {
- (&*I++)->eraseFromParent();
+ for (GlobalIFunc &IF : make_early_inc_range(M.ifuncs()))
+ if (deleteIfDead(IF, NotDiscardableComdats)) {
NumIFuncsDeleted++;
Changed = true;
}
@@ -2519,7 +2518,8 @@ optimizeGlobalsInModule(Module &M, const DataLayout &DL,
// Optimize IFuncs whose callee's are statically known.
LocalChange |= OptimizeStaticIFuncs(M);
- LocalChange |= DeleteDeadIFuncs(M);
+ // Remove any IFuncs that are now dead.
+ LocalChange |= DeleteDeadIFuncs(M, NotDiscardableComdats);
Changed |= LocalChange;
}
diff --git a/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll b/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll
index 267afa6e03114..8ddd1374e6161 100644
--- a/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll
+++ b/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function caller --check-globals all --version 4
-; RUN: opt --passes=globalopt -o - -S < %s | FileCheck %s --implicit-check-not=trivial\.ifunc
+; RUN: opt --passes=globalopt -o - -S < %s | FileCheck %s --implicit-check-not=trivial\.ifunc --implicit-check-not=dead_ifunc
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64-unknown-linux-gnu"
@@ -22,6 +22,9 @@ define void @trivial.default() {
ret void
}
+
+ at dead_ifunc.ifunc = internal ifunc void (), ptr @trivial.resolver
+
@external_ifunc.ifunc = dso_local ifunc void (), ptr @trivial.resolver
define ptr @external_ifunc.resolver() {
ret ptr @external_ifunc._Msimd
>From 6e1120186b18745160bcd16d1fc1468873b8bd29 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <jonathan_roelofs at apple.com>
Date: Mon, 5 Feb 2024 10:06:02 -0800
Subject: [PATCH 07/10] clang-format
---
llvm/lib/Transforms/IPO/GlobalOpt.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index 95a4a1f4f7b07..572969dd836fc 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -2445,7 +2445,8 @@ static bool OptimizeStaticIFuncs(Module &M) {
return Changed;
}
-static bool DeleteDeadIFuncs(Module &M, SmallPtrSetImpl<const Comdat *> &NotDiscardableComdats) {
+static bool
+DeleteDeadIFuncs(Module &M, SmallPtrSetImpl<const Comdat *> &NotDiscardableComdats) {
bool Changed = false;
for (GlobalIFunc &IF : make_early_inc_range(M.ifuncs()))
if (deleteIfDead(IF, NotDiscardableComdats)) {
>From 96d584e7f278033b1147b7b27806aecedac34d83 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <jonathan_roelofs at apple.com>
Date: Mon, 5 Feb 2024 10:07:43 -0800
Subject: [PATCH 08/10] clang-format again
---
llvm/lib/Transforms/IPO/GlobalOpt.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index 572969dd836fc..3e47a7946eca6 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -2446,7 +2446,8 @@ static bool OptimizeStaticIFuncs(Module &M) {
}
static bool
-DeleteDeadIFuncs(Module &M, SmallPtrSetImpl<const Comdat *> &NotDiscardableComdats) {
+DeleteDeadIFuncs(Module &M,
+ SmallPtrSetImpl<const Comdat *> &NotDiscardableComdats) {
bool Changed = false;
for (GlobalIFunc &IF : make_early_inc_range(M.ifuncs()))
if (deleteIfDead(IF, NotDiscardableComdats)) {
>From 53a7b10845ada99b4c8d5b335033f14a9672e595 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <jonathan_roelofs at apple.com>
Date: Mon, 5 Feb 2024 10:18:02 -0800
Subject: [PATCH 09/10] fix test copy-pasta
---
llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll b/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll
index 8ddd1374e6161..2154226cc3e8e 100644
--- a/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll
+++ b/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll
@@ -7,7 +7,7 @@ target triple = "aarch64-unknown-linux-gnu"
@trivial.ifunc = internal ifunc void (), ptr @trivial.resolver
;.
; CHECK: @unknown_condition = external local_unnamed_addr global i1
-; CHECK: @external_ifunc.ifunc = dso_local ifunc void (), ptr @trivial.resolver
+; CHECK: @external_ifunc.ifunc = dso_local ifunc void (), ptr @external_ifunc.resolver
; CHECK: @complex.ifunc = internal ifunc void (), ptr @complex.resolver
; CHECK: @sideeffects.ifunc = internal ifunc void (), ptr @sideeffects.resolver
; CHECK: @interposable.ifunc = internal ifunc void (), ptr @interposable.resolver
@@ -25,7 +25,7 @@ define void @trivial.default() {
@dead_ifunc.ifunc = internal ifunc void (), ptr @trivial.resolver
- at external_ifunc.ifunc = dso_local ifunc void (), ptr @trivial.resolver
+ at external_ifunc.ifunc = dso_local ifunc void (), ptr @external_ifunc.resolver
define ptr @external_ifunc.resolver() {
ret ptr @external_ifunc._Msimd
}
@@ -80,7 +80,7 @@ define void @interposable.default() {
define void @caller() {
; CHECK-LABEL: define void @caller() local_unnamed_addr {
; CHECK-NEXT: call void @trivial._Msimd()
-; CHECK-NEXT: call void @trivial._Msimd()
+; CHECK-NEXT: call void @external_ifunc._Msimd()
; CHECK-NEXT: call void @complex.ifunc()
; CHECK-NEXT: call void @sideeffects.ifunc()
; CHECK-NEXT: call void @interposable.ifunc()
>From 0bf69eddc673a73dec90e5eca688d65ec8de43b6 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <jonathan_roelofs at apple.com>
Date: Tue, 6 Feb 2024 09:07:06 -0800
Subject: [PATCH 10/10] also skip interposable ifuncs
---
llvm/lib/Transforms/IPO/GlobalOpt.cpp | 3 ++
.../GlobalOpt/resolve-static-ifunc.ll | 30 ++++++++++++++-----
2 files changed, 25 insertions(+), 8 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index 3e47a7946eca6..42828b4f41680 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -2407,6 +2407,9 @@ static bool OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn) {
}
static Function *hasSideeffectFreeStaticResolution(GlobalIFunc &IF) {
+ if (IF.isInterposable())
+ return nullptr;
+
Function *Resolver = IF.getResolverFunction();
if (!Resolver)
return nullptr;
diff --git a/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll b/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll
index 2154226cc3e8e..2a1717304fb4c 100644
--- a/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll
+++ b/llvm/test/Transforms/GlobalOpt/resolve-static-ifunc.ll
@@ -10,7 +10,8 @@ target triple = "aarch64-unknown-linux-gnu"
; CHECK: @external_ifunc.ifunc = dso_local ifunc void (), ptr @external_ifunc.resolver
; CHECK: @complex.ifunc = internal ifunc void (), ptr @complex.resolver
; CHECK: @sideeffects.ifunc = internal ifunc void (), ptr @sideeffects.resolver
-; CHECK: @interposable.ifunc = internal ifunc void (), ptr @interposable.resolver
+; CHECK: @interposable_ifunc.ifunc = internal ifunc void (), ptr @interposable_ifunc.resolver
+; CHECK: @interposable_resolver.ifunc = weak ifunc void (), ptr @interposable_resolver.resolver
;.
define ptr @trivial.resolver() {
ret ptr @trivial._Msimd
@@ -66,14 +67,25 @@ define void @sideeffects.default() {
ret void
}
- at interposable.ifunc = internal ifunc void (), ptr @interposable.resolver
-define weak ptr @interposable.resolver() {
- ret ptr @interposable.resolver
+ at interposable_ifunc.ifunc = internal ifunc void (), ptr @interposable_ifunc.resolver
+define weak ptr @interposable_ifunc.resolver() {
+ ret ptr @interposable_ifunc.resolver
}
-define void @interposable._Msimd() {
+define void @interposable_ifunc._Msimd() {
ret void
}
-define void @interposable.default() {
+define void @interposable_ifunc.default() {
+ ret void
+}
+
+ at interposable_resolver.ifunc = weak ifunc void (), ptr @interposable_resolver.resolver
+define ptr @interposable_resolver.resolver() {
+ ret ptr @interposable_resolver.resolver
+}
+define void @interposable_resolver._Msimd() {
+ ret void
+}
+define void @interposable_resolver.default() {
ret void
}
@@ -83,13 +95,15 @@ define void @caller() {
; CHECK-NEXT: call void @external_ifunc._Msimd()
; CHECK-NEXT: call void @complex.ifunc()
; CHECK-NEXT: call void @sideeffects.ifunc()
-; CHECK-NEXT: call void @interposable.ifunc()
+; CHECK-NEXT: call void @interposable_ifunc.ifunc()
+; CHECK-NEXT: call void @interposable_resolver.ifunc()
; CHECK-NEXT: ret void
;
call void @trivial.ifunc()
call void @external_ifunc.ifunc()
call void @complex.ifunc()
call void @sideeffects.ifunc()
- call void @interposable.ifunc()
+ call void @interposable_ifunc.ifunc()
+ call void @interposable_resolver.ifunc()
ret void
}
More information about the llvm-commits
mailing list