From 5318a4dd324a3e74130c7bd9f0b0031e1740d847 Mon Sep 17 00:00:00 2001 From: Jon Roelofs 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 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(BB.getTerminator())) + if (auto *F = dyn_cast(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 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 +@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 +} + +@unknown_condition = external global i1 +$callee_with_complex_static_resolver.resolver = comdat any +@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 +@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 +@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 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(BB.getTerminator())) - if (auto *F = dyn_cast(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(BB.getTerminator()); + if (!Ret) + return nullptr; - return Callee; + return dyn_cast(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 -@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 { +@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 -@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 { +@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 -@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 +@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 -@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 +@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 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 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 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" -@callee_with_trivial_resolver.ifunc = dso_local ifunc void (), ptr @callee_with_trivial_resolver.resolver +@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 } +@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 -@callee_with_complex_resolver.ifunc = dso_local ifunc void (), ptr @callee_with_complex_resolver.resolver +@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 } -@callee_with_sideeffects_resolver.ifunc = dso_local ifunc void (), ptr @callee_with_sideeffects_resolver.resolver +@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 } -@callee_with_interposable_resolver.ifunc = dso_local ifunc void (), ptr @callee_with_interposable_resolver.resolver +@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 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" -@callee_with_trivial_resolver.ifunc = internal ifunc void (), ptr @callee_with_trivial_resolver.resolver +@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 } -@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 +@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 -@callee_with_complex_resolver.ifunc = internal ifunc void (), ptr @callee_with_complex_resolver.resolver -define ptr @callee_with_complex_resolver.resolver() { +@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 } -@callee_with_sideeffects_resolver.ifunc = internal ifunc void (), ptr @callee_with_sideeffects_resolver.resolver -define ptr @callee_with_sideeffects_resolver.resolver() { +@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 } -@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 +@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 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 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 &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 } + +@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 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 &NotDiscardableComdats) { +static bool +DeleteDeadIFuncs(Module &M, SmallPtrSetImpl &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 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 &NotDiscardableComdats) { +DeleteDeadIFuncs(Module &M, + SmallPtrSetImpl &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 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 -@external_ifunc.ifunc = dso_local ifunc void (), ptr @trivial.resolver +@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 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 } -@interposable.ifunc = internal ifunc void (), ptr @interposable.resolver -define weak ptr @interposable.resolver() { - ret ptr @interposable.resolver +@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 +} + +@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 }