[llvm] [llvm][GlobalOpt] Optimize statically resolvable IFuncs (PR #80606)

Jon Roelofs via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 5 10:08:02 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 1/8] [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 2/8] 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 3/8] 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 4/8] 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 5/8] 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 6/8] 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 7/8] 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 8/8] 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)) {



More information about the llvm-commits mailing list